/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-05
 * V4.0
 */
package com.jphenix.servlet.parent;

import com.jphenix.driver.cluster.ServerInfoVO;
import com.jphenix.driver.json.Json;
import com.jphenix.driver.nodehandler.FNodeHandler;
import com.jphenix.driver.nodehandler.instancea.NodeHandler;
import com.jphenix.driver.threadpool.ThreadSession;
import com.jphenix.kernel.script.ScriptFieldVO;
import com.jphenix.kernel.script.ScriptUtil;
import com.jphenix.service.nodeloader.NodeService;
import com.jphenix.servlet.common.HttpServletRequestImpl;
import com.jphenix.servlet.common.ServletBeanParent;
import com.jphenix.servlet.common.SessionPara;
import com.jphenix.servlet.filter.ByteFilter;
import com.jphenix.servlet.multipart.instancea.DownloadFile;
import com.jphenix.servlet.multipart.instancea.ImgShow;
import com.jphenix.servlet.multipart.instancea.MultipartServletRequest;
import com.jphenix.servlet.multipart.instancea.UploadFile;
import com.jphenix.share.lang.*;
import com.jphenix.share.tools.Base64;
import com.jphenix.share.tools.FileCopyTools;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.share.util.ClassUtil;
import com.jphenix.share.util.DebugUtil;
import com.jphenix.share.util.StringUtil;
import com.jphenix.standard.db.QueryPageVO;
import com.jphenix.standard.docs.BeanInfo;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.docs.Register;
import com.jphenix.standard.exceptions.FixException;
import com.jphenix.standard.lang.IResult;
import com.jphenix.standard.lang.IResultRow;
import com.jphenix.standard.script.IScriptBean;
import com.jphenix.standard.script.IScriptTrusteeship;
import com.jphenix.standard.servlet.*;
import com.jphenix.standard.viewhandler.INodeHandler;
import com.jphenix.standard.viewhandler.INodeLoader;
import com.jphenix.standard.viewhandler.IViewHandler;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpSession;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 动作类父类 
 * 
 * import com.jphenix.servlet.parent.ActionBeanParent;
 * 
 * 如果是通过模板调用的动作，或是动作类中设置了模板，则返回处理后的模板（包括XML模板）
 * 
 * 如果没有设置任何模板，则返回Json格式的数据
 * 
 * 返回信息格式说明：
 * 
 * 1. 如果在请求url中增加了 _oct=xml  返回xml格式。其值还可以为json，html
 * 2. 根据请求头部的 ContentType，如果是xml，则返回xml，如果是json，则返回json
 * 
 * 2018-07-05 增加了printjs()方法输出js脚本到页面
 * 2018-07-31 这个类中的error方法时输出页面错误信息，跟基类中的error写日志方法冲突
 * 2018-08-06 增加了纯java类脚本托管父类功能，即框架调用父类中定义的_executeX方法，可由父类调用其子类脚本中执行入口_execute方法
 * 2018-10-17 增加了获取表状态和更新表状态方法
 * 2018-11-08 输入分页信息时，附带了提交当前页号、每页记录数参数主键信息
 * 2018-11-13 修改了类主键（原本是脚本中的父类主键，已经简化掉了这个脚本父类）
 * 2018-11-16 增加了直接调用的下载文件方法
 * 2018-12-06 去掉了父类中已经存在的方法，交给父类来处理
 * 2018-12-12 支持远程调用动作脚本，返回输出值对象
 * 2019-01-10 修改了指定脚本禁止输出日志时，仍然输出了日志的错误
 * 2019-01-24 将该类迁移到单独包中
 * 2019-01-25 修改了类注释
 * 2019-01-29 页面输出值方法中增加了解析SListMap对象
 * 2019-02-01 修改了uip()方法，跳过代理服务器返回真实用户IP地址
 * 2019-04-09 用SDouble替代了SFloat
 * 2019-04-16 带状态的out方法中，增加了日志输出
 * 2019-05-23 输出QueryPageVO对象时，增加了字段名序列信息
 * 2019-06-13 处理了脚本传参默认值，base64解码参数值
 * 2019-08-01 增加了页面插入值的一些方法
 * 2019-08-02 修改了获取指定返回对象时，没有设置返回数据类型的错误
 * 2019-08-09 修改了out(key,value) 方法输出整型值时，输出错误的问题
 * 2019-08-19 修改了showImg文件根路径为file_sources
 * 2019-08-20 增加了从网站根路径加载指定html文件内容并解析内容，返回解析后的内容字符串
 *            修改了输出SListMap对象
 * 2019-08-21 增加了格式化SListMap对象的方法
 * 2019-08-31 优化了代码
 * 2019-09-20 在配置文件中增加了可选配置 <no_return_error_detail>true</no_return_error_detail>，用做外部系统时，使用该参数，防止黑客发起请求，通过返回报错信息枚举出传入参数名。
 * 2019-09-24 修改了获取指定页面内容方法，支持获取框架包中指定页面内容
 * 2019-12-05 增加了两个获取提交参数对照容器方法 $m $am
 * 2019-12-13 增加了操作报文头的方法
 * 2020-03-13 修改了从url中获取参数
 * 2020-07-20 增加了out("key",null) 将null作为空字符串设置
 * 2020-07-21 将protected方法改为public方法，这样可以将动作类传入服务中去处理
 * 2020-07-22 增加了两个会话操作方法 $$$r(key)  $$$clear();
 * 2020-08-04 增加了 $u(key) 从url中获取参数值
 * 2020-09-21 格式化了局部代码，并去掉了废弃代码 ServletManager
 * 2021-03-15 增加了两个父类方法 beforeOutView 和 beforeOutJson
 * 2021-03-17 增加了几个输出类型为 text html xml json 的常用方法
 * 
 * @author 刘虻
 * 2010-6-2 下午01:36:48
 */
@ClassInfo({"2021-03-17 20:46","动作类父类"})
@BeanInfo({"webbeanparent","1","","基本动作类父类，提供常用的输出方法，根据不同情况返回Json格式，xml格式和html格式","1"})
@Register("regc")
public class ActionBeanParent extends ServletBeanParent implements IActionBean {

  protected HashMap<String,String> actionMap = null; // 动作信息容器ScriptVO
  protected HashMap<String,String> pathMap   = null; // 路径容器

  // 路径参数属性容器 key路径主键
  // value:HashMap:属性容器 key:属性主键 value:属性值
  protected Map<String,Map<String,String>> pathAttributeMap = null;

  // 动作方法对照属性容器 key动作路径 value:HashMap:
  // 属性容器key:属性主键 value:属性值
  protected Map<String,Map<String,String>>  actionAttributeMap = null;
  // 模板中的配置参数容器
  protected HashMap<String,String>          modelParameterMap = null;

  protected IActionContext ac               = null;        // 动作上下文
  protected IViewHandler   returnNodeHanler = null;        // 构建返回主界面
  protected Json           reJson           = null;        // 返回的json信息
  protected IViewHandler   reXml            = null;        // 返回XML信息
  protected boolean        noReturnInfo     = false;       // 不处理返回信息对象
  protected SDate          now              = new SDate(); // 当前时间对象
  protected NodeService    ns               = null;        // 页面对象服务
  
  //处理完当前动作后，就不再处理页面中其他的动作了
  //通常用于在页面中嵌入多个动作块，在执行其中一个动作块时
  //，重新加载了整个页面，那么后续页面内容全部发生了变化，没有办法再处理后续的动作
  protected boolean isOver      = false;
  protected boolean loadAllPage = false; // 是否加载了整个页面
  protected int     postType    = -1;    // 提交参数类型  -1未设置 0字符串  1xml  2json  3对象容器  4未知
  protected Object  postObject  = null;  // 整理后的提交参数对象

  protected Map<String,Object> actionParameterMap = null; //传入参数容器
  
  //是否初始化完毕。新构造的类默认都是false，
  //只有常驻内存的动作类，再次使用时，这个值为true
  private boolean initCompleted = false;
  
  /*
    * 如果指定模板，就不判断内容类型了，直接往模板中插入数据返回
    * 如果没指定模板，通过报文头中的内容类型来判断返回什么格式
    * 如果内容类型既不是json也不是xml，从提交参数中获取返回内容类型（_oct）
    * 如果如果提交参数中也没有设置，则设定为配置文件中默认输出(DEFAULT_OUT_CONTENT_TYPE)内容格式
    * 
    * 如果没设置默认输出格式，默认输出json格式
    */
  protected int outContentType = -1; //内容类型  -1未初始化  0html  1xml  2json

  /**
   * 构造函数
   * 
   * @author 刘虻
   */
  public ActionBeanParent() {
      super();
  }
  
  /**
   * 获取页面对象服务实例
   * @return 页面对象服务实例
   */
  protected NodeService getNodeService(){
    if(ns==null){
      ns = bean(NodeService.class);
    }
    return ns;
  }
  /**
   * 如果该动作为常驻内存动作
   * 该方法清空当前驻留内存的数据
   * 2017年3月28日
   * @author MBG
   */
  public void clear() {
      returnNodeHanler = null; // 构建返回主界面
      reJson = null;  //返回的json信息
      reXml = null; //返回XML信息
      noReturnInfo = false; //不处理返回信息对象
      if(ac!=null) {
        ac.clear();  //清除动作上下文缓存
      }
      now = new SDate(); //当前时间对象
      
      //处理完当前动作后，就不再处理页面中其他的动作了
      //通常用于在页面中嵌入多个动作块，在执行其中一个动作块时
      //，重新加载了整个页面，那么后续页面内容全部发生了变化，没有办法再处理后续的动作
      
      postType = -1; //提交参数类型  -1未设置 0字符串  1xml  2json  3对象容器  4未知
      postObject = null; //整理后的提交参数对象
    actionParameterMap = null; //传入参数容器
    
      isOver = false;
      loadAllPage = false; //是否加载了整个页面
  }
  
  /**
   * 是否不处理返回信息对象
   * @return 是否不处理返回信息对象
   * 2014年8月16日
   * @author 马宝刚
   */
  @Override
  public boolean isNoReturnInfo() {
    return noReturnInfo;
  }
  
  
  /**
   * 刷新缓存
   * @throws Exception 异常
   * 2014年8月28日
   * @author 马宝刚
   */
  public void flush() throws Exception {
    if(ac!=null) {
      ac.flush();
    }
  }
  
  /**
   * 获取输出流
   * 2014年8月28日
   * @author 马宝刚
   */
  public ServletOutputStream getStream() throws Exception {
    noReturnInfo = true; //设置忽略返回对象
    if(ac!=null) {
      return getStream();
    }
    return null;
  }
  
  /**
   * 获取信息输出对象
   * @return 信息输出对象
   * @throws Exception 异常
   * 2014年8月28日
   * @author 马宝刚
   */
  public PrintWriter getWriter() throws Exception {
    return ac.getResponse().getWriter();
  }
  
  /**
   * 直接将信息输出到界面（末尾带换行符）
   * @param content 信息
   * @throws Exception 异常
   * 2014年8月28日
   * @author 马宝刚
   */
  public void println(Object content) throws Exception {
    if(getOutContentType()==0) {
      outContent(content+"<br />");
    }else {
      outContent(content+"\n");
    }
  }
  
  /**
   * 将javascript输出到界面（实际上就是在输出的脚本内容外包上一层 <script></script>）
   * @param javaScript js脚本内容
   * @throws Exception 异常
   * 2018年7月5日
   * @author MBG
   */
  public void printjs(String javaScript) throws Exception {
    outContent("\n<script type=\"text/javascript\">"+javaScript+"</script>\n");
  }
  
  
  /**
   * 直接将信息输出到界面
   * @param contentBytes 信息
   * @throws Exception 异常
   * 2014年8月28日
   * @author 马宝刚
   */
  public void write(byte[] contentBytes) throws Exception {
    noReturnInfo = true; //设置忽略返回对象
    if(ac!=null) {
      ac.write(contentBytes);
      
    }
  }
  
  /**
   * 直接将信息输出到界面
   * @param content 信息
   * @throws Exception 异常
   * 2014年8月28日
   * @author 马宝刚
   */
  public void print(Object content) throws Exception {
    outContent(content);
  }
  

  /**
   * 直接将内容输出到页面中
   * @param content 信息对象
   * @return 当前类实例
   * @throws Exception 异常
   * 2014年8月16日
   * @author 马宝刚
   */
  public ActionBeanParent outContent(Object content) throws Exception {
    if(!noReturnInfo) {
      noReturnInfo= true;
      ac.getResponse().setCharacterEncoding("UTF-8");
    }
    if(ac!=null) {
      ac.outContent(content);
    }
    return this;
  }
  
  /**
   * 获取即将返回的Json数据对象 简称：json()
   * @return 即将返回的Json数据对象
   * 2014年4月17日
   * @author 马宝刚
   */
  @Override
  public Json getReJson() {
    outContentType = 2;
      if(reJson==null) {
          reJson = new Json();
      }
      return reJson;
  }
  
  /**
   * 获取即将返回的Json数据对象   全称：getReJson
   * @return 即将返回的Json数据对象
   * 2014年4月17日
   * @author 马宝刚
   */
  public Json json() {
    outContentType = 2;
      if(reJson==null) {
          reJson = new Json();
      }
      return reJson;
  }
  

  /**
   * 获取即将返回的XML数据对象  简称：xml()
   * @return 即将返回的XML数据对象
   * 2014年6月21日
   * @author 马宝刚
   */
  @Override
  public IViewHandler getReXml() {
    outContentType = 1;
      if(reXml==null) {
          reXml = new NodeHandler();
          reXml.setXmlStyle(true); //XML
          reXml.setDealEncode("UTF-8");
          try {
              ((NodeHandler)reXml).setNodeBody("<?xml version=\"1.0\" encoding='UTF-8'?><root id=\"root\"></root>");
          }catch(Exception e) {}
      }
      return reXml.fcid("root");
  }
  
  /**
   * 获取即将返回的XML数据对象  全称：getReXml
   * @return 即将返回的XML数据对象
   * 2014年6月21日
   * @author 马宝刚
   */
  public IViewHandler xml() {
    outContentType = 1;
      if(reXml==null) {
          reXml = new NodeHandler();
          reXml.setXmlStyle(true); //XML
          reXml.setDealEncode("UTF-8");
          try {
              ((NodeHandler)reXml).setNodeBody("<?xml version=\"1.0\" encoding='UTF-8'?><root id=\"root\"></root>");
          }catch(Exception e) {}
      }
      return reXml.fcid("root");
  }
  
  
  

  /**
   * 屏蔽过滤提交上来的html代码功能。
   * 否则提交上来的html内容会被过滤掉
   * 刘虻
   * 2012-6-24 下午4:13:42
   */
  public void enabledHtmlContent() {
      //设置启用禁止过滤代码请求
      if ($() instanceof HttpServletRequestImpl) {
          $().setDisabledWrapper(true);
      }
  }

  /**
   * 获取页面提交指定参数 刘虻 2010-7-20 上午10:48:21
   * 
   * @param parameterKey 参数主键
   * @return 参数值
   * @deprecated 不赞成使用，改为直接作为方法传入参数使用
   */
  public String $(String parameterKey) {
      // 先从页面请求中获取值
      String reStr = ac.getParameter(parameterKey);
      if (reStr.length() < 1) {
          reStr = SString.valueOf(getModelParameterMap().get(parameterKey));
      }
      return reStr;
  }
  
  /**
   * 从URL中获取请求参数值
   * @param parameterKey 参数主键
   * @return             参数值
   * 2020年8月4日
   * @author MBG
   */
  public String $u(String parameterKey) {
    return ac.getUrlParameter(parameterKey);
  }
  
  /**
   * 将参数设置到页面请求对象中（通常用在自动更新数据库值）
   * @param parameterKey 参数主键
   * @param value 参数值
   * 2016年9月26日
   * @author MBG
   */
  public void $(String parameterKey,String value) {
    ac.setParameter(parameterKey,new String[] {value});
  }

  /**
   * 获取页面提交对象 
   * 刘虻 
   * 2010-7-20 上午10:59:16
   * @return 页面提交对象
   */
  public IRequest $() {
      if(ac==null) {
          ac = (IActionContext)ThreadSession.get(IServletConst.KEY_ACTION_CONTEXT);
      }
      return ac.getRequest();
  }

  /**
   * 获取动作上下文
   */
  @Override
  public IActionContext getActionContext() {
      return ac;
  }

  /**
   * 获取页面请求对象 
   * 刘虻 
   * 2010-7-20 上午10:59:32
   * @return 页面请求对象
   */
  public IResponse $$() {
      return ac.getResponse();
  }

  /**
   * 获取页面请求对象中指定属性值 
   * 刘虻 
   * 2010-7-20 上午10:49:10
   * @param attribcuteKey 属性主键
   * @return 属性值
   */
  public Object $$(String attribcuteKey) {
      return ac.getAttribute(attribcuteKey);
  }

  /**
   * 设置属性值到页面请求对象中
   * 刘虻
   * 2010-7-20 上午10:53:26
   * @param attributeKey 属性主键
   * @param value 属性值
   */
  public void $$(String attributeKey, Object value) {
      ac.setAttribute(attributeKey, value);
  }

  /**
   * 获取会话对象中的指定属性值
   * 注意：不赞成直接使用会话，而改用会话参数对象
   * 刘虻
   * 2010-7-20 上午10:50:00
   * @param sessionAttributeKey 属性主键
   * @return 属性值
   */
  @Deprecated
  public Object $$$(String sessionAttributeKey) {
      return ac.getSessionAttribute(sessionAttributeKey);
  }

  /**
   * 设置属性值到会话对象中
   * 注意：不赞成直接使用会话，而改用会话参数对象
   * 刘虻
   * 2010-7-20 上午10:54:39
   * @param sessionAttributeKey 属性主键
   * @param value 属性值
   */
  @Deprecated
  public void $$$(String sessionAttributeKey, Object value) {
      ac.setSessionAttribute(sessionAttributeKey, value);
  }

  /**
   * 从会话中移除指定参数
   * @param sessionAttributeKey 参数主键
   * 2020年7月22日
   * @author MBG
   */
  public void $$$r(String sessionAttributeKey) {
    ac.getRequest().getSession().removeAttribute(sessionAttributeKey);
  }
  
  /**
   * 清空全部会话信息
   * 2020年7月22日
   * @author MBG
   */
  public void $$$clear() {
    ac.getRequest().getSession().invalidate();
  }
  
  
  /**
   * 获取会话对象
   * 注意：不赞成直接使用会话，而改用会话参数对象
   * 刘虻
   * 2012-11-17 下午9:03:53
   * @return 会话对象
   */
  @Deprecated
  public HttpSession $$$() {
      return ac.getRequest().getSession();
  }

  /**
   * 是否为非文本信息提交
   * 刘虻
   * 2011-5-4 下午04:29:44
   * @return true是
   */
  public boolean isMsr() {
      return ac.isMultipartRequest();
  }


  /**
   * 获取非文本信息请求对象
   * 刘虻
   * 2011-5-4 下午04:33:12
   * @return 非文本信息请求对象
   */
  public MultipartServletRequest getMsr() {
      return ac.getMultipartServletRequest();
  }

  /**
   * 设置动作上下文
   * 刘虻
   * 2010-6-30 上午11:28:54
   * @param ac 动作上下文
   */
  @Override
  public void setActionContext(IActionContext ac) {
      this.ac = ac;
      if(initCompleted) {
        clear(); //常驻内存的动作类，再次使用时需要清空历史数据
      }else {
        //首次加载该动作类
        initCompleted = true;
      }
  }

  /**
   * 在调用动作前执行的方法（由系统调用，需要时覆盖该方法）
   * 刘虻
   * 2010-6-28 下午06:26:27
   * @return 如果返回假，则掉过该方法直接返回
   * @throws Exception 异常
   */
  @Override
  public boolean beforeRunAction() throws Exception {
      return true;
  }

  /**
   * 在调用动作后执行的方法 （由系统调用，需要时覆盖该方法）
   * 刘虻
   * 2010-6-28 下午06:27:56
   * @return 返回false时，不会执行后续的将数据输出到界面。通常
   *               重写这个方法时，已经将数据输出到界面，故返回false
   * @throws Exception 异常
   */
  @Override
  public boolean afterRunAction() throws Exception {
    return true;
  }

  /**
   * 获取路径容器
   * 刘虻
   * 2010-6-2 下午07:21:18
   * @return 路径容器
   */
  public HashMap<String,String> getPathMap() {
      if (pathMap == null) {
          pathMap = new HashMap<String,String>();
      }
      return pathMap;
  }
  
  /**
   * 获取URL路径
   * @return URL路径
   * 2014年8月28日
   * @author 马宝刚
   */
  public String getUrl() {
    return ac.getRequest().getRequestURL().toString();
  }
  
  /**
   * 获取URL路径
   * @param needQueryString 是否需要返回url上提交的参数
   * @return URL路径
   * 2016年9月19日
   * @author MBG
   */
  public String getUrl(boolean needQueryString) {
    if(needQueryString) {
      //先获取提交参数信息
      String queryString = str($().getQueryString());
      if(queryString.length()>0){
        queryString = "?"+queryString;
      }
      return getUrl()+queryString;
    }
    return getUrl();
  }
  
  /**
   * 设置路径容器
   * @author 刘虻
   * 2010-6-2 下午07:21:18
   * @param pathMap 路径容器
   */
  public void setPathMap(HashMap<String,String> pathMap) {
      if (pathMap == null) {
          return;
      }
      if (this.pathMap == null) {
          this.pathMap = new HashMap<String,String>();
      }
      // 这样父类的配置文件中配置的PathMap，子类中也配置了，不会覆盖父类配置的信息
      this.pathMap.putAll(pathMap);
  }

  /**
   * 设置路径容器（包含设置路径属性）
   * @author 刘虻
   * 2010-6-2 下午07:21:18
   * @param xml 配置信息块XML对象
   */
  public void setPathMap(IViewHandler xml) {
      // 路径属性容器
      pathAttributeMap = new HashMap<String, Map<String, String>>();
      // 路径容器
      HashMap<String,String> pathMap = new HashMap<String, String>();
      // 获取信息行
      List<IViewHandler> xmls = xml.getChildDealNodes();
      String key; //路径主键
      for (IViewHandler cXml:xmls) {
        key = cXml.a("key");
        pathMap.put(key, cXml.nt());
        // 获取节点属性容器
        pathAttributeMap.put(key, cXml.getAttributeMap());
      }
      setPathMap(pathMap);
  }

  /**
   * 获取指定路径指定属性值
   * @author 刘虻
   * 2010-6-2 下午07:14:12
   * @param pathPK 路径属性
   * @param key 属性主键
   * @return 属性值
   */
  public String getPathAttribute(String pathPK, String key) {
      return SString.valueOf(getPathAttributeMap(pathPK).get(key));
  }

  /**
   * 获取路径信息属性容器
   * @author 刘虻
   * 2010-6-2 下午07:14:12
   * @param pathPK 路径属性
   * @return 路径信息属性容器
   */
  public Map<String,String> getPathAttributeMap(String pathPK) {
      if (pathAttributeMap == null) {
          pathAttributeMap = new HashMap<String, Map<String, String>>();
      }
      // 指定路径属性容器
      Map<String,String> pMap = pathAttributeMap.get(pathPK);
      if (pMap == null) {
          return new HashMap<String,String>();
      }
      return pMap;
  }

  /**
   * 获取有效的html对象加载类
   * 刘虻
   * 2010-6-2 下午07:14:12
   * @return 有效的html对象加载类
   */
  public INodeLoader getNodeLoader() throws Exception {
      return getNodeService().getNodeLoader();
  }

  /**
   * 获取视图对象类实例
   * 刘虻
   * 2010-6-2 下午07:10:45
   * @param path 视图相对路径
   * @return 视图对象类实例
   */
  public INodeHandler getNodeHandler(String path) {
      try {
          return getNodeService().loadNode(path,ac);
      } catch (Exception e) {
        e.printStackTrace();
        log
        .error(i("A0001",
          "Get New ViewHandler Path:[<?>] Exception:[<?>]",
          new String[] { path,
          DebugUtil.getExceptionMessage(e) }), e);
      }
      return null;
  }

  /**
   * 设置动作路径信息容器(包含动作属性信息)
   * @author 刘虻
   * 2010-6-2 下午07:10:45
   * @param xml XML信息对象
   */
  public void setActionMapFromXml(IViewHandler xml) {
      // 路径属性容器
      actionAttributeMap = new HashMap<String, Map<String, String>>();
      // 路径容器
      HashMap<String,String> actionMap = new HashMap<String,String>();
      // 获取信息行
      List<IViewHandler> xmls = xml.getChildDealNodes();
      String key; //路径值主键
      for (IViewHandler cXml:xmls) {
          key = cXml.a("key");
          actionMap.put(key, cXml.nt());
          // 获取节点属性容器
          actionAttributeMap.put(key,cXml.getAttributeMap());
      }
      setActionMap(actionMap);
  }

  /**
   * 获取指定动作路径的指定属性值
   * @author 刘虻
   * 2010-6-2 下午07:10:45
   * @param actionKey 动作路径
   * @param key 属性主键
   * @return 属性值
   */
  public String getActionAttribute(String actionKey, String key) {
      return SString.valueOf(getActionAttributeMap(actionKey).get(key));
  }

  /**
   * 原getParameterMap中的参数值为字符串数组，用起来不方便
   * 刘虻
   * 2012-12-18 上午9:09:23
   * @return 页面提交参数容器。
   */
  public Map<String,String> getSingleParameterMap() {
      return ac.getSingleParameterMap();
  }

  /**
   * 通过动作路径获取指定的属性容器
   * @author 刘虻 2010-6-2 下午07:10:45
   * @param actionKey 动作路径
   * @return 指定的属性容器
   */
  public Map<String,String> getActionAttributeMap(String actionKey) {
      if (actionAttributeMap == null) {
          actionAttributeMap = new HashMap<String, Map<String, String>>();
      }
      // 获取指定动作路径的属性容器
      Map<String,String> cAttributeMap = actionAttributeMap.get(actionKey);
      if (cAttributeMap == null) {
          return new HashMap<String,String>();
      }
      return cAttributeMap;
  }

  /**
   * 设置动作路径对照容器
   * 刘虻
   * 2010-6-2 下午07:28:37
   * @param actionMap 动作路径对照容器
   */
  public void setActionMap(HashMap<String,String> actionMap) {
      this.actionMap = actionMap;
  }

  /**
   * 覆盖方法
   * 刘虻
   * 2010-6-2 下午01:36:48
   */
  @Override
  public Map<String,String> getActionMap() {
    if (actionMap == null) {
      actionMap = new HashMap<String,String>();
    }
    return actionMap;
  }

  /**
   * 获取一个新的主界面 原方法名 getNewViewHandler
   * 刘虻
   * 2010-6-17 上午10:35:59
   * @return 一个新的主界面
   */
  @Override
  public INodeHandler newVh() {
    outContentType = 0;
    return getNodeService().getNewViewHandler();
  }

  /**
   * 获取返回主界面  简写：vh()
   * 刘虻
   * 2010-6-17 上午10:34:56
   * @return 返回主界面
   */
  @Override
  public IViewHandler getReVh() {
    if (returnNodeHanler == null) {
      returnNodeHanler = newVh();
    }
    return returnNodeHanler;
  }
  
  /**
   * 获取返回主界面 原方法名：getReVh
   * @return
   * 2016年11月8日
   * @author MBG
   */
  public IViewHandler vh() {
      if (returnNodeHanler == null) {
          returnNodeHanler = newVh();
      }
      return returnNodeHanler;
  }
  
  /**
   * 加载指定页面作为返回页面
   * @param path 页面相对路径（相对于网站根路径）
   * @return 页面对象
   * 2016年11月8日
   * @author MBG
   */
  public IViewHandler loadPage(String path) {
    return loadPage(path,false);
  }
  
  /**
   * 加载指定页面作为返回页面
   * @param path 页面相对路径（相对于网站根路径）
   * @param allPage 这个页面是否为完整的页面（通常一个页面文件中只有整个页面中的一小块代码）
   * @return 页面对象
   * 2016年11月8日
   * @author MBG
   */
  public IViewHandler loadPage(String path,boolean allPage) {
    INodeHandler loadPageVh;
    try {
          //构建返回值
      loadPageVh = getNodeService().loadNode(path,ac);
    }catch(Exception e) {
      e.printStackTrace();
      loadPageVh = newVh();
    }
    if(allPage) {
      loadAllPage = true;
      isOver = true; //重新加载了页面，后续同级的块动作就没必要再继续处理了
      if(getReVh().isEmpty()) {
        setReVh(loadPageVh);
      }else {
        getReVh().getBaseNode().clear().os(false).ac(loadPageVh);
      }
      return getReVh();
    }else {
      setReVh(loadPageVh);
      
      return loadPageVh;
    }
  }
  
  /**
   * 根据视图主键获取视图类实例 原方法名：getViewHandlerByID
   * 刘虻
   * 2010-6-17 上午10:36:39
   * @param viewID 视图主键
   * @return 视图类实例
   * @throws Exception 执行发生异常
   */
  @Override
  public IViewHandler getVh(String viewID) throws Exception {
      // 获取视图主键对应的路径
      String path = SString.valueOf(getPathMap().get(viewID));
      if (path.length() < 1) {
          throw new Exception(i("A0006",
            "Not Find The ViewHandler By ViewID:[<?>]",
            new String[] { viewID }));
      }
      // 构建返回值
      INodeHandler reNh = null;
      try {
          reNh = getNodeService().getNodeHandlerByPath(ac,path);
      } catch (Exception e) {
          throw new Exception(i("A0007",
            "Get The ViewHandler By VIewID:[<?>] Exception:[<?>]",
            new String[] { viewID, DebugUtil.getExceptionMessage(e) }));
      }
      return reNh;
  }

  /**
   * 通过视图主键获取页面对象，并设置为返回主页面 源方法名
   * 刘虻
   * 2012-12-4 下午2:18:33
   * @param viewID 页面主键  setPathMap 中的key
   * @return 设置为返回主页面后的页面对象
   * @throws Exception 异常
   */
  @Override
  public IViewHandler setReVh(String viewID) throws Exception {
      return setReVh(getVh(viewID));
  }

  /**
   * 重定向到指定路径
   * @param path 指定路径
   * @throws Exception 异常
   * 2015年1月13日
   * @author 刘虻
   */
  public void redir(String path) throws Exception {
      ac.getResponse().sendRedirect(path);
  }

  /**
   * 设置返回主界面 原方法名：setReturnViewHandler
   * 刘虻
   * 2010-6-17 上午10:35:37
   * @param vh 返回主界面
   * @return 返回主界面（与传入参数相同，方便写程序用）
   */
  @Override
  public IViewHandler setReVh(IViewHandler nh) {
      returnNodeHanler = nh;
      outContentType = 0;
      return returnNodeHanler;
  }

  /**
   * 模板中的配置参数容器
   * 刘虻
   * 2010-8-5 下午02:18:41
   * @return 模板中的配置参数容器
   */
  public HashMap<String,String> getModelParameterMap() {
      if (modelParameterMap == null) {
          modelParameterMap = new HashMap<String,String>();
      }
      return modelParameterMap;
  }

  /**
   * 设置模板中的参数信息容器
   * 刘虻
   * 2010-8-5 下午02:16:07
   * @param parameterMap 模板中的参数信息容器
   */
  @Override
  public void setModelParameterMap(Map<String,String> parameterMap) {
      modelParameterMap = new HashMap<String,String>();
      modelParameterMap.putAll(parameterMap);
  }

  /**
   *  一个动作的每个环节异常处理方法
   * 刘虻
   * 2010-6-8 下午02:15:24
   * @param e 异常
   */
  @Override
  public void errorAction(Exception e) {
      error(DebugUtil.getExceptionMessage(e));
  }

  /**
   * 输出错误信息
   * @param errorMsg 错误信息
   * 2014年6月23日
   * @author 马宝刚
   */
  public void outError(String errorMsg) {
    error(errorMsg);
    if(errorMsg.length()>0) {
      //获取返回页面
      switch(getOutContentType()) {
      case 0:
        if(returnNodeHanler.hasChildNodeByID("msg")) {
          returnNodeHanler.getFirstChildNodeByID("msg").nt(errorMsg);
        }
        break;
      case 1:
        //获取返回XML对象
        IViewHandler errorVh = getReXml();
        if(errorVh.hasChildNodeByID("msg")) {
            errorVh.fcid("msg").nt(errorMsg);
        }else if(errorVh.hasChildNodeByNodeName("msg")) {
          errorVh.fnn("msg").nt(errorMsg);
        }else {
          errorVh.newInstance().nn("msg").a("id","msg").nt(errorMsg);
        }
        if(errorVh.hasChildNodeByID("status")) {
          errorVh.fcid("status").nt("-1");
        }else if(errorVh.hasChildNodeByNodeName("status")) {
          errorVh.fnn("status").nt("-1");
        }else {
          errorVh.newInstance().nn("status").a("id","status").nt("-1");
        }
        break;
      default:
        getReJson()
          .putString("msg",errorMsg)
          .putString("status","-1");
      }
    }
  }

  /**
   * 获取Cookie值
   * @param key 主键
   * @return 值
   * 2014年5月28日
   * @author 马宝刚
   */
  public String cookie(String key) {
      return ac.getCookie(key);
  }

  /**
   * 设置Cookie值
   * @param key          主键
   * @param value       值
   * 2014年5月28日
   * @author 马宝刚
   */
  public void cookie(String key,String value) {
      ac.setCookie(key,value);
  }
  
  /**
   * 删除Cookie值
   * @param key 主键
   * 2017年9月20日
   * @author MBG
   */
  public void cookieDel(String key) {
    ac.delCookie(key);
  }

  /**
   * 设置输出格式为文本
   */
  public void typeText(){
    ac.getResponse().setContentType("text/plain");
  }

  /**
   * 设置输出格式为XML
   */
  public void typeXml(){
    ac.getResponse().setContentType("text/xml");
  }

  /**
   * 设置输出格式为HTML
   */
  public void typeHtml(){
    ac.getResponse().setContentType("text/html");
  }

  /**
   * 设置输出格式为Json
   */
  public void typeJon(){
    ac.getResponse().setContentType("application/json");
  }

  /**
   * 设置输出指定格式
   * @param type 格式描述
   */
  public void outType(String type){
    ac.getResponse().setContentType(type);
  }
  
  /**
   * 设置Cookie信息（默认最长时间1年，全局域）
   * @param key          主键
   * @param value       值
   * @param path        域（相对路径）
   * @param outTime  过期时间（秒）
   * 2017年8月21日
   * @author MBG
   */
  public void cookie(String key,String value,String path,int outTime) {
    ac.setCookie(key,value,path,outTime);
  }

  /**
   * 将数据类设置到页面中（范围：整个页面）
   * 注意：数据类（Bean）中的变量，只能是基本的几个类型，不能是序列，容器之类的
   * @param vhID 页面范围节点主键
   * @param bean 数据类（取变量的值，并不是通过get方法取值）
   * 2014年5月19日
   * @author 马宝刚
   */
  public void out(String vhID,Object bean) {
      out(vhID,bean,null);
  }


  /**
   * 将数据类设置到页面中（范围：整个页面）
   * 注意：数据类（Bean）中的变量，只能是基本的几个类型，不能是序列，容器之类的
   * @param bean 数据类（取变量的值，并不是通过get方法取值）
   * 2014年5月19日
   * @author 马宝刚
   */
  public void out(Object bean) {
      out(null,bean,null);
  }
  
  
  /**
   * 输出结果到页面
   * @param status 执行状态
   * @param msg    信息提示
   * @param bean   输出值
   * 2016年10月2日
   * @author MBG
   */
  public void out(int status, String msg, Object bean) {
    info("status:[" + status + "] " + msg);
    if (getOutContentType() == 1) {
      outXml("status", String.valueOf(status), null);
      outXml("msg", msg, null);
    } else {
      outJson("status", String.valueOf(status), null);
      outJson("msg", msg, null);
    }
    if (bean != null) {
      out(bean);
    }
  }

  
  /**
   * 输出结果到页面
   * @param status 执行状态
   * @param msg    信息提示
   * @param vhId   数据主键
   * @param bean   输出值
   * 2016年10月2日
   * @author MBG
   */
  public void out(int status,String msg,String vhID,Object bean) {
    info("status:["+status+"] "+msg);
      if(getOutContentType()==1) {
          outXml("status",String.valueOf(status),null);
          outXml("msg",msg,null);
      }else {
          outJson("status",String.valueOf(status),null);
          outJson("msg",msg,null);
      }
      if(bean!=null) {
        out(vhID,bean);
      }
  }
  
  /**
   * 输出结果到页面
   * @param status 执行状态
   * @param msg    信息提示
   * 2016年10月2日
   * @author MBG
   */
  public void out(int status,String msg) {
      out(status,msg,null);
  }
  
  
  /**
   * 输出状态信息到页面并输出到控制台
   * @param status 状态代码
   * @param msg 输出信息
   * @param ext 输出到日志中的补充信息（不会输出到页面）
   * 2015年4月2日
   * @author 马宝刚
   */
  public void outs(int status,String msg,String... ext) {
      out(status,msg);
      String logMsg = ""; //日志
      if(ext!=null && ext.length>0 && ext[0]!=null && ext[0].length()>0) {
          logMsg += ext[0]+" ";
      }
      log(logMsg+msg+"("+status+")");
  }

  /**
   * 将序列数据设置到Json对象中
   * @param cid               json 子节点主键
   * @param dataList       记录集序列
   * 2014年6月19日
   * @author 马宝刚
   */
  @SuppressWarnings({ "unchecked", "rawtypes" })
private void setJsonData(String cid,List<?> dataList) {
      if(dataList==null) {
          return;
      }
      Json json;
      if(cid!=null && cid.length()>0) {
        json = getReJson().getChildJson(cid);
      }else {
        json = getReJson();
      }
      json
      .putString("allcount",String.valueOf(dataList.size()))
      .putString("fno","1")
      .putString("tno",String.valueOf(dataList.size()));
      json = json.getChildJson("rs"); //记录集
      if(dataList.size()>0) {
        Object rowData; //行记录数据
          Map rowDataMap; //行数据元素
          for(int i=0;i<dataList.size();i++) {
            rowData = dataList.get(i); //行记录集
              if(rowData==null) {
                continue;
              }else if(rowData instanceof Map) {
                rowDataMap = (Map)rowData;
              }else if(rowData instanceof Json) {
                  rowDataMap = ((Json)rowData).getValueMap();
              }else if(rowData instanceof IResultRow) {
                rowDataMap = ((IResultRow)rowData)._getMap();
              }else {
                //其他类型
                json.addValue(rowData);
                continue;
              }
              rowDataMap.put("_index",String.valueOf(i)); //从0开始
              rowDataMap.put("_no",String.valueOf(i+1)); //从1开始
              json.addMapValue(rowDataMap);
          }
      }
  }
  
  /**
   * 将序列数据设置到Json对象中
   * @param cid               json 子节点主键
   * @param qpVO         分页记录集序列
   * 2014年6月19日
   * @author 马宝刚
   */
  @SuppressWarnings({ "unchecked", "rawtypes" })
  private void setJsonData(String cid,QueryPageVO qpVO) {
      if(qpVO==null) {
          return;
      }
      Json json;
      if(cid!=null && cid.length()>0) {
          json = getReJson().getChildJson(cid);
      }else {
          json = getReJson();
      }
      json
          .putString("allcount",String.valueOf(qpVO.allCount))
          .putString("fno",String.valueOf(qpVO.fromId))
          .putString("tno",String.valueOf(qpVO.toId))
          .putString("pageno",String.valueOf(qpVO.pageNo))
          .putString("pagesize",String.valueOf(qpVO.pageSize))
          .putString("pagecount",String.valueOf(qpVO.pageCount))
          .putString("keyPageNo",str(qpVO.keyPageNo))
          .putString("keyPageSize",str(qpVO.keyPageSize));
      
      if(qpVO.fields!=null && qpVO.fields.size()>0) {
        Json fieldJ = json.put("fields");
        for(String fName:qpVO.fields) {
          fieldJ.add(fName);
        }
      }
      
      json = json.getChildJson("rs"); //记录集
      if(qpVO.rs.size()>0) {
          Object rowData; //行记录数据
          Map rowDataMap; //行数据元素
          for(int i=0;i<qpVO.rs.size();i++) {
              rowData = qpVO.rs.get(i); //行记录集
              if(rowData==null) {
                  continue;
              }else if(rowData instanceof Map) {
                  rowDataMap = (Map)rowData;
              }else if(rowData instanceof Json) {
                  rowDataMap = ((Json)rowData).getValueMap();
              }else if(rowData instanceof IResultRow) {
                  rowDataMap = ((IResultRow)rowData)._getMap();
              }else {
                  json.addValue(str(rowData));
                  continue;
              }
              rowDataMap.put("_index",String.valueOf(i)); //从0开始
              rowDataMap.put("_no",String.valueOf(i+1)); //从1开始
              json.addMapValue(rowDataMap);
          }
      }
  }

  /**
   * 将序列数据设置到XML对象中
   * @param cid               xml 子节点主键
   * @param qpVO          分页记录集序列
   * 2014年6月19日
   * @author 马宝刚
   */
  @SuppressWarnings({ "unchecked", "rawtypes" })
  public void setXmlData(String cid,QueryPageVO qpVO) {
      if(qpVO==null) {
          return;
      }
      IViewHandler xml = getReXml(); //获取主xml对象
      if(cid!=null && cid.length()>0) {
          if(xml.hasChildNodeByID(cid)) {
              xml = xml.fcid(cid);
          }else {
              xml = xml.newInstance().nn(cid).a("id",cid);
          }
      }
      xml
      .a("allcount",String.valueOf(qpVO.allCount))
      .a("fno",String.valueOf(qpVO.fromId))
      .a("tno",String.valueOf(qpVO.toId))
      .a("pageno",String.valueOf(qpVO.pageNo))
      .a("pagesize",String.valueOf(qpVO.pageSize))
      .a("pagecount",String.valueOf(qpVO.pageCount))
      .a("keyPageNo",str(qpVO.keyPageNo))
      .a("keyPageSize",str(qpVO.keyPageSize));
      
      if(qpVO.fields!=null && qpVO.fields.size()>0) {
        IViewHandler fields = xml.newInstance().nn("fields");
        for(String fName:qpVO.fields) {
          fields.ac(xml.newInstance().nn("field").nt(fName));
        }
        xml.ac(fields);
      }
      
      xml = xml.newInstance().nn("rs").a("id","rs"); //记录集
      if(qpVO.rs.size()>0) {
          Object rowData; //行记录数据
          Map<String,?> rowDataMap; //行数据元素
          IViewHandler rowXml; //行对象
          List<String> keyList; //字段主键序列
          for(int i=0;i<qpVO.rs.size();i++) {

              rowXml = xml.newInstance().nn("row").a("id",String.valueOf(i));

              rowData = qpVO.rs.get(i); //行记录集
              if(rowData==null) {
                  continue;
              }else if(rowData instanceof Map) {
                  rowDataMap = (Map)rowData;
              }else if(rowData instanceof Json) {
                  rowDataMap = ((Json)rowData).getValueMap();
              }else if(rowData instanceof IResultRow) {
                  rowDataMap = ((IResultRow)rowData)._getMap();
              }else {
                  log.warning("Out Data Content Element Not Map Or IResultRow",null);
                  continue;
              }
              keyList = BaseUtil.getMapKeyList(rowDataMap);

              //放入索引
              rowXml.newInstance().nn("_index").nt(String.valueOf(i)); //从0开始
              rowXml.newInstance().nn("_no").nt(String.valueOf(i+1)); //从1开始
              for(String fieldPK:keyList) {
                  rowXml.newInstance().nn(fieldPK).a("id",fieldPK).cdata(SString.valueOf(rowDataMap.get(fieldPK)));
              }
          }
      }
      setOutXml();
  }

  /**
   * 将序列数据设置到XML对象中
   * @param cid               xml 子节点主键
   * @param dataList       记录集序列
   * 2014年6月19日
   * @author 马宝刚
   */
  @SuppressWarnings({ "unchecked", "rawtypes" })
public void setXmlData(String cid,List<?> dataList) {
      if(dataList==null) {
          return;
      }
      IViewHandler xml = getReXml(); //获取主xml对象
      if(cid!=null && cid.length()>0) {
          if(xml.hasChildNodeByID(cid)) {
              xml = xml.fcid(cid);
          }else {
              xml = xml.newInstance().nn(cid).a("id",cid);
          }
      }
      xml
        .a("allcount",String.valueOf(dataList.size()))
        .a("fno","1")
        .a("tno",String.valueOf(dataList.size()));
      xml = xml.newInstance().nn("rs").a("id","rs"); //记录集
      if(dataList.size()>0) {
        Object rowData; //行记录数据
          Map<String,?> rowDataMap; //行数据元素
          IViewHandler rowXml; //行对象
          List<String> keyList; //字段主键序列
          for(int i=0;i<dataList.size();i++) {

              rowXml = xml.newInstance().nn("row").a("id",String.valueOf(i));

              rowData = dataList.get(i); //行记录集
              if(rowData==null) {
                continue;
              }else if(rowData instanceof Map) {
                rowDataMap = (Map)rowData;
              }else if(rowData instanceof Json) {
                  rowDataMap = ((Json)rowData).getValueMap();
              }else if(rowData instanceof IResultRow) {
                rowDataMap = ((IResultRow)rowData)._getMap();
              }else {
                log.warning("Out Data Content Element Not Map Or IResultRow",null);
                continue;
              }
              keyList = BaseUtil.getMapKeyList(rowDataMap);

              //放入索引
              rowXml.newInstance().nn("_index").nt(String.valueOf(i)); //从0开始
              rowXml.newInstance().nn("_no").nt(String.valueOf(i+1)); //从1开始
              for(String fieldPK:keyList) {
                  rowXml.newInstance().nn(fieldPK).a("id",fieldPK).cdata(SString.valueOf(rowDataMap.get(fieldPK)));
              }
          }
      }
      setOutXml();
  }

  
  /**
   * 将数据序列设置到页面指定位置
   * 注意：页面行主键为：tr   即 id="tr"
   * @param vhID             页面范围节点主键
   * @param qpVO           分页数据序列  Element:Map:key:主键 value:值
   * @return 设置后的页面对象
   * 2014年5月19日
   * @author 马宝刚
   */
  @SuppressWarnings({ "unchecked", "rawtypes" })
  private void outList(String vhID,QueryPageVO qpVO) {
      if (returnNodeHanler == null) {
          if(getOutContentType()==1) {
              setXmlData(vhID,qpVO);
          }else {
              setJsonData(vhID,qpVO);
          }
          return;
      }
      //获取即将设置数据的页面表对象
      IViewHandler tableVh = returnNodeHanler;
      if(vhID!=null) {
          if(tableVh.hasChildNodeByID(vhID)) {
              tableVh = tableVh.getFirstChildNodeByID(vhID);
          }else {
              //如果设置了插入值范围（指定往某个节点插入数据），但是又不存在这个节点，则不做任何操作
              return;
          }
      }
      if(qpVO==null) {
          return;
      }
      //设置分页信息，到表节点属性中
      tableVh
        .a("allcount",String.valueOf(qpVO.allCount))
        .a("fno",String.valueOf(qpVO.fromId))
        .a("tno",String.valueOf(qpVO.toId))
        .a("pageno",String.valueOf(qpVO.pageNo))
        .a("pagesize",String.valueOf(qpVO.pageSize))
        .a("pagecount",String.valueOf(qpVO.pageCount))
        .a("keyPageNo",str(qpVO.keyPageNo))
        .a("keyPageSize",str(qpVO.keyPageSize));
      
      String rowID = "tr"; //行主键
      IViewHandler trModel = tableVh.mcid(rowID); //获取行模板
      //判断是否显示行模板节点本身（不影响内部节点）
      if(SBoolean.valueOf(trModel.a("_hiddenself"))) {
          trModel.os(false);
      }
      if(qpVO.rs.size()>0) {
          Object rowData; //行记录对象
          Map rowDataMap; //行记录元素
          for(int i=0;i<qpVO.rs.size();i++) {
              rowData = qpVO.rs.get(i);
              if(rowData==null) {
                  continue;
              }else if(rowData instanceof Map) {
                  rowDataMap = (Map)rowData;
              }else if(rowData instanceof Json) {
                  rowDataMap = ((Json)rowData).getValueMap();
              }else if(rowData instanceof IResultRow) {
                  rowDataMap = ((IResultRow)rowData)._getMap();
              }else {
                  //获取参数值
                  Map<String,Object> objMap = ClassUtil.getObjectVarMap(rowData);
                  List<String> keyList = BaseUtil.getMapKeyList(objMap); //参数主键
                  rowDataMap = new HashMap();
                  for(String key:keyList) {
                      rowDataMap.put(key,SString.valueOf(objMap.get(key)));
                  }
              }
              rowDataMap.put("_index",String.valueOf(i)); //从0开始
              rowDataMap.put("_no",String.valueOf(i+1)); //从1开始
              IViewHandler tr = trModel.cn(); //构建行节点
              setDetailMapByNs(null,null,rowDataMap,tr); //设置行数据 
              tableVh.ac(tr); //放入表中
          }
          tableVh.rcid("_nodata");
      }
  }

  /**
   * 将数据序列设置到页面指定位置
   * 注意：页面行主键为：tr   即 id="tr"
   * @param vhID             页面范围节点主键
   * @param dataList         数据序列  Element:Map:key:主键 value:值
   * @return 设置后的页面对象
   * 2014年5月19日
   * @author 马宝刚
   */
  @SuppressWarnings({ "unchecked", "rawtypes" })
private void outList(String vhID,List<?> dataList) {
      if (returnNodeHanler == null) {
          if(getOutContentType()==1) {
              setXmlData(vhID,dataList);
          }else {
              setJsonData(vhID,dataList);
          }
          return;
      }
      //获取即将设置数据的页面表对象
      IViewHandler tableVh = returnNodeHanler;
      if(vhID!=null) {
          if(tableVh.hasChildNodeByID(vhID)) {
              tableVh = tableVh.getFirstChildNodeByID(vhID);
          }else {
              //如果设置了插入值范围（指定往某个节点插入数据），但是又不存在这个节点，则不做任何操作
              return;
          }
      }
      if(dataList==null) {
          return;
      }
      //设置分页信息，到表节点属性中
      tableVh
        .a("allcount",String.valueOf(dataList.size()))
        .a("fno","1")
        .a("tno",String.valueOf(dataList.size()));
      String rowID = "tr"; //行主键
      IViewHandler trModel = tableVh.mcid(rowID); //获取行模板
      //判断是否显示行模板节点本身（不影响内部节点）
      if(SBoolean.valueOf(trModel.a("_hiddenself"))) {
          trModel.os(false);
      }
      if(dataList.size()>0) {
        Object             rowData;    //行记录对象
          Map                rowDataMap; //行记录元素
          Map<String,Object> objMap;     //行记录元素对象
          List<String>       keyList;    //参数主键
          IViewHandler       tr;         //行元素
          for(int i=0;i<dataList.size();i++) {
            rowData = dataList.get(i);
            if(rowData==null) {
              continue;
            }else if(rowData instanceof Map) {
              rowDataMap = (Map)rowData;
            }else if(rowData instanceof Json) {
                rowDataMap = ((Json)rowData).getValueMap();
              }else if(rowData instanceof IResultRow) {
                rowDataMap = ((IResultRow)rowData)._getMap();
              }else if(rowData instanceof String) {
                rowDataMap = new HashMap<String,String>();
                rowDataMap.put("_ele", rowData);
              }else {
                //获取参数值
                objMap     = ClassUtil.getObjectVarMap(rowData);
                keyList    = BaseUtil.getMapKeyList(objMap); //参数主键
                rowDataMap = new HashMap();
                for(String key:keyList) {
                  rowDataMap.put(key,SString.valueOf(objMap.get(key)));
                }
              }
              rowDataMap.put("_index",String.valueOf(i)); //从0开始
              rowDataMap.put("_no",String.valueOf(i+1)); //从1开始
              tr = trModel.cn(); //构建行节点
              setDetailMapByNs(null,null,rowDataMap,tr); //设置行数据 
              tableVh.ac(tr); //放入表中
          }
          tableVh.rcid("_nodata");
      }
  }
  
  /**
   * 输出指定值
   * @param vhID 模板主键
   * @param key  参数主键
   * @param value 参数值
   * 2014年8月27日
   * @author 马宝刚
   */
  public void out(String vhID,String key,String value) {
    //构建参数容器
    Map<String,String> dataMap = new HashMap<String,String>();
    dataMap.put(key,value);
    setDetailMapByNs(null,vhID,dataMap,null);
  }
  
  
  /**
   * 输出指定值
   * @param vhID 模板主键
   * @param key  参数主键
   * @param value 参数值
   * @param ns   命名空间主键
   * 2014年8月27日
   * @author 马宝刚
   */
  public void out(String vhID,String key,String value,String ns) {
    //构建参数容器
    Map<String,String> dataMap = new HashMap<String,String>();
    dataMap.put(key,value);
    setDetailMapByNs(ns,vhID,dataMap,null);
  }

  /**
   * 将记录集序列放入指定列表节点
   * @param rs        记录集序列
   * @param tableId   列表节点主键
   * @param trId      列表中行模板主键
   * @param noDataId  列表中没有记录提示行主键
   * 2019年8月1日
   * @author MBG
   */
  public void out(List<Map<String,?>> rs,String tableId,String trId,String noDataId) {
    if(returnNodeHanler==null) {
      returnNodeHanler = newVh();
    }
    //获取表对象
    IViewHandler tb = returnNodeHanler.fcid(tableId);
    if(tb.isEmpty()) {
      return;
    }
    IViewHandler noData = null; //没有记录提示行
    if(noDataId!=null && noDataId.length()>0) {
      noData = tb.rcid(noDataId);
    }
    IViewHandler tr = tb.rcid(trId); //行模板
    if(rs==null || rs.size()<1) {
      tb.ac(noData);
      return;
    }
    IViewHandler trEle; //行记录元素
    for(Map<String,?> ele:rs) {
      trEle = tr.cn();
      out(ele,trEle);
      tb.ac(trEle);
    }
  }
  
  /**
   * 将容器中的数据插入到指定页面块中
   * @param data      数据容器
   * @param vh        指定页面块
   * 2019年8月1日
   * @author MBG
   */
  public void out(Map<String,?> data,IViewHandler setVh) {
    out(data,setVh,null);
  }
  
  /**
   * 将容器中的数据插入到指定页面块中
   * @param data      数据容器
   * @param vh        指定页面块
   * @param nameSpace 如果节点存在命名空间值，则只设置明明空间值匹配的节点
   * 2019年8月1日
   * @author MBG
   */
  public void out(Map<String,?> data,IViewHandler setVh,String nameSpace) {
      if(setVh==null) {
          if (returnNodeHanler == null) {
              returnNodeHanler = newVh();
          }
          //要处理的模板对象
          setVh = returnNodeHanler;
      }
      if(data==null) {
        data = new HashMap<String,String>();
      }
      List<IViewHandler> vhs = setVh.getAttribNodes("_field"); //获取需要设置数据的节点序列
      String nsNs;  //节点中的命名空间值
      boolean hasSet; //是否设置了值
      String fieldPK; //字段主键
      String fieldValue; //字段值
      String checkType; //是否需要处理数据值
      String keyWord; //需要将默认属性值中的关键字替换成将要插入的数据   关键字
      String setAttr; //需要设置的属性名
      String nodeName; //获取节点名
      String fieldPKs; //字段主键序列
      for(IViewHandler vh:vhs) {
        fieldPKs = vh.a("_field");
        
        //如果 _field="_hidden" 则不显示当前标签（仍然显示其内部的标签）
        if("_hidden".equals(fieldPKs)) {
          vh.setOutSelf(false);
          continue;
        }
        
        hasSet = false;
          nsNs = vh.a("_ns");
          if(nameSpace!=null 
                  && nameSpace.length()>0 
                  && !"*".equals(nsNs)
                  && !nsNs.equals(nameSpace)) {
              //命名空间不匹配，不做处理
              continue;
          }
          //获取需要设置数据的字段主键，多个主键用逗号分割
          //一个节点可以设置多个数据值。比如<a href="数据1">数据2</a>
          List<String> fieldPKList = BaseUtil.splitToList(fieldPKs,",");
          if(fieldPKList.size()<1) {
              continue;
          }
          if(fieldPKList.size()<2) {
              //只需要设置一个字段值
              fieldPK = SString.valueOf(fieldPKList.get(0)); //字段主键
              if(data.containsKey(fieldPK)) {
                hasSet = true;
                fieldValue = SString.valueOf(data.get(fieldPK)); //字段值
                checkType = vh.a("_checktype"); //是否需要处理数据值
                if(checkType.length()<1) {
                    checkType = vh.a("_check");
                }
                keyWord = vh.a("_keyword"); //需要将默认属性值中的关键字替换成将要插入的数据   关键字

                if(checkType.length()>0) {
                  //字符串截取，日期时间类型整理等等
                  fieldValue = StringUtil.checkValue(checkType,fieldValue,vh);
                  if(fieldValue==null) {
                    continue;
                  }
                }
                setAttr = vh.a("_set"); //需要设置的属性名

                if(setAttr.length()>0) {
                  //指定了需要设置值的属性名
                  if(keyWord.length()>0) {
                    //将默认属性值中的关键字替换成对应的值
                    fieldValue = BaseUtil.swapString(vh.a(setAttr),keyWord,fieldValue);
                  }
                  vh.a(setAttr,fieldValue);
                }else {
                  nodeName = vh.nn().toLowerCase(); //获取节点名
                  if("input".equals(nodeName)) {
                    String atype = vh.a("type").toLowerCase(); //输入框类型
                    if("image".equals(atype)) {
                      vh.a("src",fieldValue);
                    }else if("checkbox".equals(atype) || "radio".equals(atype)) {
                      if(vh.a("value").equals(fieldValue)) {
                        vh.p("checked");
                      }
                    }else {
                      if(keyWord.length()>0) {
                        //将默认属性值中的关键字替换成对应的值
                        fieldValue = BaseUtil.swapString(vh.a("value"),keyWord,fieldValue);
                      }
                      vh.a("value",fieldValue);
                    }
                  }else if("select".equals(nodeName)) {
                    List<IViewHandler> cvhs = vh.c(); //所有的子节点
                    for(IViewHandler cvh:cvhs) {
                      if(!"option".equalsIgnoreCase(cvh.nn())) {
                        continue;
                      }
                      if(cvh.a("value").equals(fieldValue)) {
                        cvh.p("selected");
                        break;
                      }
                    }
                  }else {
                    if(keyWord.length()>0) {
                      //将默认属性值中的关键字替换成对应的值
                      fieldValue = BaseUtil.swapString(vh.nt(),keyWord,fieldValue);
                    }
                    vh.nt(fieldValue);
                  }
                }
              }
          }else {
              //一个节点需要设置多个数据值
              for(int j=0;j<fieldPKList.size();j++) {
                  fieldPK = SString.valueOf(fieldPKList.get(j));
                  if(!data.containsKey(fieldPK)) {
                    continue;
                  }
                  hasSet = true;
                  fieldValue = SString.valueOf(data.get(fieldPK)); //字段值
                  checkType = vh.a("_checktype"+(j+1)); //是否需要处理数据值
                  if(checkType.length()<1) {
                      checkType = vh.a("_check"+(j+1));
                  }
                  keyWord = vh.a("_keyword"+(j+1)); //需要将默认属性值中的关键字替换成将要插入的数据   关键字
                  if(checkType.length()>0) {
                      //字符串截取，日期时间类型整理等等
                      fieldValue = StringUtil.checkValue(checkType,fieldValue,vh);
                      if(fieldValue==null) {
                          continue;
                      }
                  }
                  setAttr = vh.a("_set"+(j+1)); //需要设置的属性名
                  if(setAttr.length()>0) {
                      //指定了需要设置值的属性名
                      if(keyWord.length()>0) {
                          //将默认属性值中的关键字替换成对应的值
                          fieldValue = BaseUtil.swapString(vh.a(setAttr),keyWord,fieldValue);
                      }
                      vh.a(setAttr,fieldValue);
                  }else {
                      nodeName = vh.nn().toLowerCase(); //获取节点名
                      if("input".equals(nodeName)) {
                          String atype = vh.a("type").toLowerCase(); //输入框类型
                          if("image".equals(atype)) {
                              vh.a("src",fieldValue);
                          }else if("checkbox".equals(atype) || "radio".equals(atype)) {
                              if(vh.a("value").equals(fieldValue)) {
                                  vh.p("checked");
                              }
                          }else {
                              if(keyWord.length()>0) {
                                  //将默认属性值中的关键字替换成对应的值
                                  fieldValue = BaseUtil.swapString(vh.a("value"),keyWord,fieldValue);
                              }
                              vh.a("value",fieldValue);
                          }
                      }else if("select".equals(nodeName)) {

                          List<IViewHandler> cvhs = vh.c(); //所有的子节点
                          for(IViewHandler cvh:cvhs) {
                              if(cvh==null || !"option".equalsIgnoreCase(cvh.nn())) {
                                  continue;
                              }
                              if(cvh.a("value").equals(fieldValue)) {
                                  cvh.p("selected");
                              }
                          }
                      }else {
                          if(keyWord.length()>0) {
                              //将默认属性值中的关键字替换成对应的值
                              fieldValue = BaseUtil.swapString(vh.nt(),keyWord,fieldValue);
                          }
                          vh.nt(fieldValue);
                      }
                  }
              }
          }
          if(hasSet) {
              //如果设置了不显示当前标签，只显示其标签的内容  <div>内容</div>
              if(SBoolean.valueOf(vh.a("_hiddenself"))) {
                vh.setOutSelf(false);
              }
          }
      }
  
  }



  /**
   * 将数据类（bean）设置到页面中
   * @param vhID         页面范围节点主键
   * @param bean         数据类（取变量的值，并不是通过get方法取值）  String  List<Map<String,String> IResult  IResultRow  Map<String,String>
   * @param nameSpace 命名空间（有时候需要将多个数据设置到同一个页面区域，多个数据的主键名可能相同，需要用命名空间来区分）
   * @return                 设置后的页面对象
   * 2014年5月19日
   * @author 马宝刚
   */
  @SuppressWarnings({ "unchecked", "rawtypes" })
  public void out(String vhID, Object bean, String nameSpace) {
    if (bean == null) {
      outString(vhID, "", null);
    } else if (bean instanceof String) {
      outString(vhID, (String) bean, null);
    } else if (bean instanceof Integer || bean instanceof Long || bean instanceof Double || bean instanceof Float
        || bean instanceof Boolean) {
      outString(vhID, SString.valueOf(bean), null);
      // 发现之前是这样写的 bean instanceof Integer ，这样写，说不通啊，为啥整型值的key不起作用呢。
      // //直接输出状态值
      // if(getOutContentType()==1) {
      // outXml("status",String.valueOf(bean),null);
      // }else {
      // outJson("status",String.valueOf(bean),null);
      // }
    } else if (bean instanceof Map) {
      setDetailMapByNs(nameSpace, vhID, (Map<String, String>) bean, null);
    } else if (bean instanceof QueryPageVO) {
      outList(vhID, (QueryPageVO) bean);
    } else if (bean instanceof List) {
      outList(vhID, (List<?>) bean);
    } else if (bean instanceof SListMap) {
      // outList(vhID,((SListMap<?>)bean).values()); //不能这么用，否则Map中的值无法输出
      setDetailSListMapByNs(nameSpace, vhID, (SListMap<?>) bean, null);
    } else if (bean instanceof IResult) {
      outList(vhID, ((IResult) bean)._getResult());
    } else if (bean instanceof IResultRow) {
      setDetailMapByNs(nameSpace, vhID, ((IResultRow) bean)._getMap(), null);
    } else if (bean instanceof SortVector) {
      outList(vhID, ((SortVector) bean).toList());
    } else if (bean instanceof IViewHandler) {
      setOutXml(); // 标记输出xml格式
      if (((IViewHandler) bean).isXmlStyle()) {
        if (vhID == null || vhID.length() < 1) {
          reXml = (IViewHandler) bean;
          reXml.setDealEncode("UTF-8");
        } else {
          IViewHandler xml = getReXml(); // 获取主xml对象
          if (xml.hasChildNodeByID(vhID)) {
            xml = xml.fcid(vhID);
          } else {
            xml = xml.newInstance().nn(vhID).a("id", vhID);
          }
          xml.ac((IViewHandler) bean);
        }
      } else {
        if (vhID == null || vhID.length() < 1) {
          returnNodeHanler = (IViewHandler) bean;
          returnNodeHanler.setDealEncode("UTF-8");
        } else {
          getReVh().fcid(vhID).ac((IViewHandler) bean);
        }
      }
    } else if (bean instanceof Json) {
      setOutJson(); // 标记输出json格式
      // 转换为Json对象
      Json paraJson = (Json) bean;
      if (returnNodeHanler == null) {
        if (vhID == null || vhID.length() < 1) {
          getReJson().setJson(paraJson);
        } else {
          getReJson().putJson(vhID, paraJson);
        }
      } else {
        if (paraJson.isList()) {
          outList(vhID, paraJson.getChildList());
        } else {
          setDetailMapByNs(nameSpace, vhID, paraJson.getValueMap(), null);
        }
      }
    } else if (bean instanceof String[]) {
      outString(vhID, StringUtil.arr2str((String[]) bean), null);
    } else {
      setDetailMapByNs(nameSpace, vhID, ClassUtil.getObjectVarMap(bean), null);
    }
  }


  /**
   * 设置值到Json对象中
   * @param key          参数主键
   * @param value        参数值
   * @param json         指定json对象
   * 2014年6月19日
   * @author 马宝刚
   */
  private void outJson(String key,String value,Json json) {
      if(json==null) {
          json = getReJson();
      }
      setOutJson();
      json.putString(key,value);
  }
  
  /**
   * 设置值到XML对象中
   * @param cid           子节点主键
   * @param value       值
   * @param xml          指定xml对象
   * 2014年6月28日
   * @author 马宝刚
   */
  private void outXml(String cid,String value,IViewHandler xml) {
      if(xml==null) {
          xml = getReXml();
      }
      setOutXml();
      if(cid!=null && cid.length()>0) {
          if(xml.hasChildNodeByID(cid)) {
              xml.fcid(cid).nt(value);
          }else {
              xml.newInstance().nn(cid).a("id",cid).nt(value);
          }
      }
  }

  /**
   * 将单个数据设置到页面指定节点中
   * @param vhID         节点主键
   * @param value        值
   * @param pVh          父节点对象
   * @return                 指定节点对象
   * 2014年5月19日
   * @author 马宝刚
   */
  private void outString(String vhID,String value,IViewHandler pVh) {
      if(pVh==null) {
          if(returnNodeHanler==null) {
              if(getOutContentType()==1) {
                  outXml(vhID,value,null);
              }else {
                  outJson(vhID,value,null);
              }
              return;
          }else {
              pVh = returnNodeHanler;
          }
      }
      List<IViewHandler> pVhList;
      if(vhID==null || vhID.length()<1) {
        pVhList = new ArrayList<IViewHandler>();
        pVhList.add(pVh);
      }else {
          if(pVh.hasChildNodeByID(vhID)) {
            pVhList = pVh.getChildNodesByID(vhID);
          }else {
              //如果设置了插入值范围（指定往某个节点插入数据），但是又不存在这个节点，则不做任何操作
              return;
          }
      }
      for(IViewHandler ele:pVhList) {
          //如果设置了不显示当前标签，只显示其标签的内容  <div>内容</div>
          if(SBoolean.valueOf(ele.a("_hiddenself")) || SBoolean.valueOf(ele.a("_hidden"))) {
            ele.setOutSelf(false);
          }
          String checkType = ele.a("_checktype"); //是否需要处理数据值
          if(checkType.length()<1) {
              checkType = ele.a("_check"); //新的属性 被简化
          }
          String keyWord = ele.a("_keyword"); //需要将默认属性值中的关键字替换成将要插入的数据   关键字
          if(checkType.length()>0) {
              //字符串截取，日期时间类型整理等等
              value = StringUtil.checkValue(checkType,value,ele);
              if(value==null) {
                  return;
              }
          }
          String setAttr = ele.a("_set"); //需要设置的属性名
          if(setAttr.length()>0) {
              //指定了需要设置值的属性名
              if(keyWord.length()>0) {
                  //将默认属性值中的关键字替换成对应的值
                  value = BaseUtil.swapString(ele.a(setAttr),keyWord,value);
              }
              ele.a(setAttr,value);
          }else {
              String nodeName = ele.nn().toLowerCase(); //获取节点名
              if("input".equals(nodeName)) {
                  String atype = ele.a("type").toLowerCase(); //输入框类型
                  if("image".equals(atype)) {
                      ele.a("src",value);
                  }else if("checkbox".equals(atype) || "radio".equals(atype)) {
                      if(ele.a("value").equals(value)) {
                          ele.p("checked");
                      }
                  }else {
                      if(keyWord.length()>0) {
                          //将默认属性值中的关键字替换成对应的值
                          value = BaseUtil.swapString(ele.a("value"),keyWord,value);
                      }
                      ele.a("value",value);
                  }
              }else if("select".equals(nodeName)) {
                  List<IViewHandler> cvhs = ele.c(); //所有的子节点
                  for(IViewHandler cvh:cvhs) {
                      if(!"option".equalsIgnoreCase(cvh.nn())) {
                          continue;
                      }
                      if(cvh.a("value").equals(value)) {
                          cvh.p("selected");
                      }
                  }
              }else {
                  if(keyWord.length()>0) {
                      //将默认属性值中的关键字替换成对应的值
                      value = BaseUtil.swapString(ele.nt(),keyWord,value);
                  }
                  ele.nt(value);
              }
          }
      }
  }

  /**
   * 将提交上来的参数设置回页面中
   * @return 设置后的页面对象
   * 2014年6月12日
   * @author 马宝刚
   */
  public void outRequest() {
      outRequest(null,null);
  }

  /**
   * 将提交上来的参数设置回页面中
   * @param vhID 指定页面范围主键
   * @return 设置后的页面对象
   * 2014年6月12日
   * @author 马宝刚
   */
  public void outRequest(String vhID) {
      outRequest(vhID,null);
  }


  /**
   * 将提交上来的参数设置回页面中
   * @param vhID 指定页面范围主键
   * @param nameSpace 命名空间主键
   * @return 设置后的页面对象
   * 2014年6月12日
   * @author 马宝刚
   */
  public void outRequest(String vhID,String nameSpace) {
      setDetailMapByNs(nameSpace,vhID,ac.getSingleParameterMap(),null);
  }



  /**
   * 将容器值设置到JSon对象中
   * @param cid       json子主键
   * @param detailMap 数据对象容器
   * @param json      指定Json对象
   * 2014年6月19日
   * @author 马宝刚
   */
  private void setJsonDetailMap(String cid,Map<String,?> detailMap,Json json) {
      if(detailMap==null) {
          return;
      }
      if(json==null) {
          json = getReJson();
      }
      if(cid!=null && cid.length()>0) {
          json = json.getChildJson(cid);
      }
      json.putValueMap(detailMap);
  }
  
  /**
   * 将容器值设置到JSon对象中
   * @param cid      json子主键
   * @param listMap  数据对象容器
   * @param json     指定Json对象
   * 2019年8月21日
   * @author MBG
   */
  private void setJsonDetailSListMap(String cid,SListMap<?> listMap,Json json) {
      if(listMap==null) {
          return;
      }
      if(json==null) {
          json = getReJson();
      }
      if(cid!=null && cid.length()>0) {
          json = json.getChildJson(cid);
      }
      String key; //主键元素
      for(int i=0;i<listMap.size();i++) {
        key = listMap.getKey(i);
        json.put(key,listMap.get(key));
      }
  }


  /**
   * 将容器值设置到XML对象中
   * @param cid       XML子主键
   * @param detailMap 数据对象容器
   * @param xml       指定xml对象
   * 2014年6月19日
   * @author 马宝刚
   */
  private void setXmlDetailMap(String cid,Map<String,?> detailMap,IViewHandler xml) {
      if(detailMap==null) {
          return;
      }
      if(xml==null) {
          xml = getReXml();
      }
      if(cid!=null && cid.length()>0) {
          if(xml.hasChildNodeByID(cid)) {
              xml = xml.fcid(cid);
          }else {
              xml = xml.newInstance().nn(cid).a("id",cid);
          }
      }
      //主键序列
      List<String> keyList = BaseUtil.getMapKeyList(detailMap);
      for(String fieldPK:keyList) {
          xml.newInstance().nn(fieldPK).a("id",fieldPK).cdata(SString.valueOf(detailMap.get(fieldPK)));
      }
  }
  
  /**
   * 将容器值设置到XML对象中
   * @param cid      XML子主键
   * @param listMap  数据对象容器
   * @param xml      指定xml对象
   * 2019年8月21日
   * @author MBG
   */
  private void setXmlDetailSListMap(String cid,SListMap<?> listMap,IViewHandler xml) {
    if(listMap==null) {
      return;
    }
    if(xml==null) {
      xml = getReXml();
    }
      if(cid!=null && cid.length()>0) {
          if(xml.hasChildNodeByID(cid)) {
              xml = xml.fcid(cid);
          }else {
              xml = xml.newInstance().nn(cid).a("id",cid);
          }
      }
      String key; //主键
      for(int i=0;i<listMap.size();i++) {
        key = listMap.getKey(i);
        xml.newInstance().nn(key).a("id",key).cdata(str(listMap.get(key)));
      }
  }
  

  /**
   * 将容器中的值设置到页面对象中
   * 刘虻
   * 2012-7-5 下午3:02:28
   * @param ns 命名空间名字   在java中设置数据时，可以传入一个空间名，
   * 如果该界面也有一个空间名，则程序会判断两个名字是否相同，如果相同，
   * 才会设置值。这样就实现了在java中，往页面中同时设置多个数据包，避
   * 免数据包中的数据主键重复
   * 
   * @param nodeID 页面对象中指定的节点id
   * @param detailMap 信息容器
   * @param mainVh 将信息设置到指定模板对象中
   */
  public void setDetailMapByNs(String ns,String nodeID,Map<String,?> detailMap,IViewHandler mainVh) {
      if(mainVh==null) {
          if (returnNodeHanler == null) {
              if(getOutContentType()==1) {
                  setXmlDetailMap(nodeID,detailMap,null);
              }else {
                  setJsonDetailMap(nodeID,detailMap,null);
              }
              return;
          }
          //要处理的模板对象
          mainVh = returnNodeHanler;
      }
      if(nodeID!=null) {
          if(mainVh.hasChildNodeByID(nodeID)) {
              mainVh = mainVh.getFirstChildNodeByID(nodeID);
          }else {
              //如果设置了插入值范围（指定往某个节点插入数据），但是又不存在这个节点，则不做任何操作
              return;
          }
      }
      out(detailMap,mainVh,ns); //设置节点值
  }
  
  
  /**
   * 将容器中的值输出
   * @param ns        命名空间名字   在java中设置数据时，可以传入一个空间名，
   * 如果该界面也有一个空间名，则程序会判断两个名字是否相同，如果相同，
   * 才会设置值。这样就实现了在java中，往页面中同时设置多个数据包，避
   * 免数据包中的数据主键重复
   * 
   * @param nodeID    页面对象中指定的节点id
   * @param listMap   序列信息容器
   * @param mainVh    将信息设置到指定模板对象中
   * 2019年8月21日
   * @author MBG
   */
  public void setDetailSListMapByNs(
      String ns,String nodeID,SListMap<?> listMap,IViewHandler mainVh) {
      if(mainVh==null) {
          if (returnNodeHanler == null) {
              if(getOutContentType()==1) {
                setXmlDetailSListMap(nodeID,listMap,null);
              }else {
                setJsonDetailSListMap(nodeID,listMap,null);
              }
              return;
          }
          //要处理的模板对象
          mainVh = returnNodeHanler;
      }
      if(nodeID!=null) {
          if(mainVh.hasChildNodeByID(nodeID)) {
              mainVh = mainVh.getFirstChildNodeByID(nodeID);
          }else {
              //如果设置了插入值范围（指定往某个节点插入数据），但是又不存在这个节点，则不做任何操作
              return;
          }
      }
      out(listMap.toMap(),mainVh,ns); //设置节点值
  }

  /**
   * 设置输出XML格式（注意：该方法必须在动作方法最开始处执行）
   * 
   * 2014年6月22日
   * @author 马宝刚
   */
  public void setOutXml() {
      returnNodeHanler = null;
      outContentType = 1;
  }

  /**
   * 设置输出Json格式（注意：该方法必须在动作方法最开始处执行）
   * 
   * 2014年6月22日
   * @author 马宝刚
   */
  public void setOutJson() {
      returnNodeHanler = null;
      outContentType = 2;
  }

  /**
   * 设置输出类型 
   * @param contentType  0html  1xml  2json
   * 2018年12月12日
   * @author MBG
   */
  @Override
  public void setContentType(int contentType) {
    outContentType = contentType;
    returnNodeHanler = null;
  }
  
  /**
   * 获取返回内容类型  0html 1xml 2json  <br />
   *  <br />
   * 如果指定模板，就不判断内容类型了，直接往模板中插入数据返回  <br />
   * 如果没指定模板，通过报文头中的内容类型来判断返回什么格式  <br />
   * 如果内容类型既不是json也不是xml，从提交参数中获取返回内容类型（_oct）  <br />
   * 如果如果提交参数中也没有设置，则设定为配置文件中默认输出(DEFAULT_OUT_CONTENT_TYPE)内容格式  <br />
   *  <br />
   * 如果没设置默认输出格式，默认输出json格式  <br />
   * @return
   * 2014年6月21日
   * @author 马宝刚
   */
  @Override
  public int getOutContentType() {
      if(returnNodeHanler!=null) {
          return 0;
      }
      if(outContentType<0) {
        if(ac==null) {
          outContentType = 2;
          return outContentType;
        }
          //从报文头中获取返回内容类型
          String contentType = SString.valueOf(ac.getRequest().getContentType()).toLowerCase();
          if(contentType.length()>0) {
              if(contentType.indexOf("xml")>-1) {
                  outContentType = 1;
              }else if(contentType.indexOf("json")>-1) {
                  outContentType = 2;
              }else {
                  //是否直接为数字，如果不是数字，默认为2json
                  outContentType = SInteger.valueOf(contentType);
                  if(outContentType!=1 && outContentType!=2) {
                      //从提交参数中获取返回内容类型
                      contentType = ac.getUrlParameter("_oct");
                      if(contentType.indexOf("xml")>-1) {
                          outContentType = 1;
                      }else if(contentType.indexOf("json")>-1) {
                          outContentType = 2;
                      }else {
                          //从配置文件中获取默认输出内容格式
                          contentType = SString.valueOf(prop.getParameter("DEFAULT_OUT_CONTENT_TYPE")).toLowerCase();
                          if(contentType.length()<1) {
                              //未设置，默认输出json格式
                              outContentType = 2;
                          }else if(contentType.indexOf("xml")>-1) {
                              outContentType = 1;
                          }else if(contentType.indexOf("json")>-1) {
                              outContentType = 2;
                          }else {
                              //是否直接为数字，如果不是数字，默认为2json
                              outContentType = SInteger.valueOf(contentType);
                              if(outContentType!=1 && outContentType!=2) {
                                  outContentType = 2;
                              }
                          }
                      }
                  }
              }
          }else {
              //从提交参数中获取返回内容类型
              contentType = ac.getUrlParameter("_oct");
              if(contentType.indexOf("xml")>-1) {
                  outContentType = 1;
              }else if(contentType.indexOf("json")>-1) {
                  outContentType = 2;
              }else {
                  //从配置文件中获取默认输出内容格式
                  contentType = SString.valueOf(prop.getParameter("DEFAULT_OUT_CONTENT_TYPE")).toLowerCase();
                  if(contentType.length()<1) {
                      //未设置，默认输出json格式
                      outContentType = 2;
                  }else if(contentType.indexOf("xml")>-1) {
                      outContentType = 1;
                  }else if(contentType.indexOf("json")>-1) {
                      outContentType = 2;
                  }else {
                      //是否直接为数字，如果不是数字，默认为2json
                      outContentType = SInteger.valueOf(contentType);
                      if(outContentType!=1 && outContentType!=2) {
                          outContentType = 2;
                      }
                  }
              }
          }
      }
      return outContentType;
  }

  /**
   * 从会话中移出指定类，通过类名
   * @param sessionBeanName 类名
   * 2014年6月23日
   * @author 马宝刚
   */
  public void invalidateSession(String sessionBeanName) {
      $$$().removeAttribute(sessionBeanName);
  }
  
  
  /**
   * 整理调用动作的参数 这么做主要是为了在调用beforeRunAction时，能取到即将传入的值，有些值只能获取一次 ，比如提交的XML对象或者Json对象
   * 
   * @param paraMap 指定设置的参数值容器 key，参数名 value参数值 （优先使用该容器中的值）
   * @throws Exception 异常 2014年8月1日
   * @author 马宝刚
   */
  @Override
  public void buildActionParameter(Map<String, ?> paraMap) throws Exception {
    // 日志信息缓存
    StringBuffer logSbf = new StringBuffer();

    // 脚本动作类 注意：脚本动作类传参只能传入Map类型参数

    // 是否不向前台输出详细报错信息。通常用于外网访问时，需要在配置文件中设置
    // <no_return_error_detail>true</no_return_error_detail>
    boolean noReturnErrorDetail = boo(p("no_return_error_detail"));

    // 参数名序列
    List<ScriptFieldVO> paraNameList = ((IScriptBean) this).getParameterList();

    actionParameterMap = new HashMap<String, Object>(); // 传入参数

    logSbf.append("InvokeScript[" + ((IScriptBean) this).getScriptId() + "] Parameters:\n");
    ScriptFieldVO field; // 参数元素
    Object paraValue; // 参数值
    for (int i = 0; i < paraNameList.size(); i++) {
      field = paraNameList.get(i);
      logSbf.append("(").append(i + 1).append(") Type:【").append(field.type).append("】 Name:【").append(field.name)
          .append("】NotNull:【").append(field.notNull).append("】 DefaultValue: 【").append(field.defValue)
          .append("】 value:【");

      paraValue = null;
      if ("string".equalsIgnoreCase(field.type) || "int".equalsIgnoreCase(field.type)
          || "boolean".equalsIgnoreCase(field.type)) {
        ////////////////////////// String int boolean
        ////////////////////////// //////////////////////////////////////////////////////
        // 从参数容器中获取值
        if (paraMap != null) {
          paraValue = paraMap.get(field.name);
        }
        if (paraValue == null && ac != null) {
          paraValue = ac.getParameter(field.name);
        }
        if ((paraValue == null || "".equals(paraValue)) && field.defValue != null && field.defValue.length() > 0) {
          paraValue = field.defValue;
        }
        if (field.needB64Dec) {
          paraValue = Base64.base64Decode(SString.valueOf(paraValue), "UTF-8");
        }
        if (field.notNull && "".equals(paraValue)) {
          // 用字段说明构造错误信息
          String msg = StringUtil.cutString(field.explain, "//");
          if (msg == null || msg.length() < 1) {
            msg = "参数[" + field.name + "]";
          }
          if (noReturnErrorDetail) {
            out(-98, "传入参数错误");
          } else {
            out(-98, msg + "，不能为空", new Json("{\"key\":\"" + field.name + "\",\"title\":\"" + field.explain + "\"}"));
          }
          warning(msg + "，不能为空 key:[" + field.name + "] title:[" + field.explain + "]");
          throw new FixException();
        }
        actionParameterMap.put(field.name, paraValue);

        if (postObject == null) {
          // 标记提交参数为字符串
          postType = 0;
          if (paraMap == null && ac != null) {
            postObject = ac.getSingleParameterMap();
          } else {
            postObject = paraMap;
          }
        }

        logSbf.append(paraValue).append("】\n");
      } else if ("json".equalsIgnoreCase(field.type)) {
        ////////////////////////// json//////////////////////////////////////////////////////
        // 构建返回值对象
        Json paraJson = null;
        if (paraMap != null) {
          paraJson = (Json) paraMap.get(field.name);
        }
        if (paraJson == null) {
          // 编码格式
          String enc = null;
          if (ac != null) {
            enc = ac.getRequest().getHeader("encoding");
            if (enc == null || enc.length() < 1) {
              enc = ac.getRequest().getCharacterEncoding();
            }
          }
          if (enc == null || enc.length() < 1) {
            enc = "UTF-8";
          }
          // 构建返回值对象
          paraJson = new Json();
          if (ac != null) {
            // 如果提交了json格式的数据，自动开放html代码过滤
            ac.getRequest().setDisabledWrapper(true);
            if (ac.getRequest().getContentLength() > 0) {
              if (field.needB64Dec) {
                paraJson.setString(
                    Base64.base64Decode(FileCopyTools.copyToString(ac.getRequest().getInputStream(), enc), "UTF-8"));
              } else {
                paraJson.setString(FileCopyTools.copyToString(ac.getRequest().getInputStream(), enc));
              }
            }
          }
          if (paraJson.size() < 1 && field.defValue != null && field.defValue.length() > 0) {
            if (field.needB64Dec) {
              paraJson.setString(Base64.base64Decode(field.defValue, "UTF-8"));
            } else {
              paraJson.setString(field.defValue);
            }
          }
          if (field.notNull && paraJson.size() < 1) {
            // 用字段说明构造错误信息
            String msg = StringUtil.cutString(field.explain, "//");
            if (msg == null || msg.length() < 1) {
              msg = "参数[" + field.name + "]";
            }
            if (noReturnErrorDetail) {
              out(-98, "传入参数错误");
            } else {
              out(-98, msg + "，不能为空",
                  new Json("{\"key\":\"" + field.name + "\",\"title\":\"" + field.explain + "\"}"));
            }
            warning(msg + "，不能为空 key:[" + field.name + "] title:[" + field.explain + "]");
            throw new FixException();
          }
        }
        actionParameterMap.put(field.name, paraJson);

        if (postObject == null) {
          // 设置提交参数类型以及对象
          postType = 2;
          postObject = paraJson;
        }

        logSbf.append(paraJson).append("】\n");
      } else if ("xml".equalsIgnoreCase(field.type) || "IViewHandler".equalsIgnoreCase(field.type)) {
        //////////////////////////////////////////// xml
        //////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
        // 构建返回值对象
        INodeHandler paraXml = null;
        if (paraMap != null) {
          paraXml = (INodeHandler) paraMap.get(field.name);
        }
        if (paraXml == null) {
          // 编码格式
          String enc = null;
          if (ac != null) {
            enc = ac.getRequest().getHeader("encoding");
            if (enc == null || enc.length() < 1) {
              enc = ac.getRequest().getCharacterEncoding();
            }
          }
          if (enc == null || enc.length() < 1) {
            enc = "UTF-8";
          }
          // 构建XML对象
          paraXml = FNodeHandler.newNodeHandler();
          paraXml.setXmlStyle(true);
          if (ac != null && ac.getRequest().getContentLength() > 0) {
            // 直接将读入流设置到xml对象中
            if (field.needB64Dec) {
              paraXml.setNodeBody(
                  Base64.base64Decode(FileCopyTools.copyToString(ac.getRequest().getInputStream()), "UTF-8"));
            } else {
              paraXml.setStream(ac.getRequest().getInputStream(), "ServletExplorer");
            }
          }
          if (paraXml.isEmpty() && field.defValue != null && field.defValue.length() > 0) {
            if (field.needB64Dec) {
              paraXml.setNodeBody(Base64.base64Decode(field.defValue, "UTF-8"));
            } else {
              paraXml.setNodeBody(field.defValue);
            }
          }
          if (field.notNull && paraXml.isEmpty()) {
            // 用字段说明构造错误信息
            String msg = StringUtil.cutString(field.explain, "//");
            if (msg == null || msg.length() < 1) {
              msg = "参数[" + field.name + "]";
            }
            if (noReturnErrorDetail) {
              out(-98, "传入参数错误");
            } else {
              out(-98, msg + "，不能为空",
                  new Json("{\"key\":\"" + field.name + "\",\"title\":\"" + field.explain + "\"}"));
            }
            warning(msg + "，不能为空 key:[" + field.name + "] title:[" + field.explain + "]");
            throw new FixException();
          }
        }
        actionParameterMap.put(field.name, paraXml);

        if (postObject == null) {
          // 设置提交参数类型以及对象
          postType = 1;
          postObject = paraXml;
        }

        logSbf.append(paraXml).append("】\n");
      } else if (field.type.startsWith("@")) {
        /////////////////////////////// bean id
        /////////////////////////////// /////////////////////////////////////////////////////////
        if (paraMap != null) {
          paraValue = paraMap.get(field.name);
        }
        if (paraValue == null) {
          // 类型为类主键
          paraValue = getBeanFactory().getObject(field.type.substring(1), this);
          if (ac != null && paraValue instanceof SessionPara) {
            ((SessionPara) paraValue).setActionContext(ac);
          }
          if (field.notNull && paraValue == null) {
            // 用字段说明构造错误信息
            String msg = StringUtil.cutString(field.explain, "//");
            if (msg == null || msg.length() < 1) {
              msg = "参数[" + field.name + "]";
            }
            if (noReturnErrorDetail) {
              out(-98, "传入参数错误");
            } else {
              out(-98, msg + "，不能为空",
                  new Json("{\"key\":\"" + field.name + "\",\"title\":\"" + field.explain + "\"}"));
            }
            warning(msg + "，不能为空 key:[" + field.name + "] title:[" + field.explain + "]");
            throw new FixException();
          }
        }
        actionParameterMap.put(field.name, paraValue);
        logSbf.append(paraValue).append("】\n");

        if (postObject == null) {
          // 设置提交参数类型以及对象
          postType = 3;
          postObject = actionParameterMap;
        }

      } else {
        ///////////////////////////////////// other params
        ///////////////////////////////////// ////////////////////////////////////////////////////
        if (paraMap != null) {
          paraValue = paraMap.get(field.name);
        }
        if (paraValue == null) {
          // 特殊参数值
          if (ac != null) {
            paraValue = setParams(ScriptUtil.getTypeCls(field.type), field.name, ac, "", field.notNull);
          }
          if (field.notNull && paraValue == null) {
            // 用字段说明构造错误信息
            String msg = StringUtil.cutString(field.explain, "//");
            if (msg == null || msg.length() < 1) {
              msg = "参数[" + field.name + "]";
            }
            if (noReturnErrorDetail) {
              out(-98, "传入参数错误");
            } else {
              out(-98, msg + "，不能为空",
                  new Json("{\"key\":\"" + field.name + "\",\"title\":\"" + field.explain + "\"}"));
            }
            warning(msg + "，不能为空 key:[" + field.name + "] title:[" + field.explain + "]");
            throw new FixException();
          }
        }
        actionParameterMap.put(field.name, paraValue);

        if (postObject == null) {
          // 设置提交参数类型以及对象
          postType = 4;
          postObject = actionParameterMap;
        }

        logSbf.append(paraValue).append("】\n");
      }
    }
    if (outLog()) {
      // 如果已经设置为不输出日志，就不要在禁用输出日志了，也避免过一会儿打开
      if (ac != null && !SBoolean.valueOf(ac.getRequest().getUrlParameter("_nolog_"))) {
        if (((IScriptBean) this).noOutLog()) {
          outLog(false);
        } else {
          // 强制输出日志，避免当前线程之前意外报错没有打开日志输出
          outLog(true);
        }
      }
    }
    debug(logSbf.toString()); // 输出日志
  }
  
  
  
  /**
   * 执行动作
   * @param paraMap 指定设置的参数值容器 key，参数名  value参数值 （优先使用该容器中的值）
   * @throws Exception 异常
   * 2014年8月1日
   * @author 马宝刚
   */
@Override
  public void invokeAction() throws Exception {
  //获取调用方法
    if(this instanceof IScriptBean) {
      if(this instanceof IScriptTrusteeship) {
        ((IScriptTrusteeship)this)._executeXN(actionParameterMap);
      }else {
        ((IScriptBean)this)._executeN(actionParameterMap);
      }
    }else{
      //传统类需要重写这个方法
    }
  }
  
  
  /**
   * 从Request中获取参数值
   * @param cls         参数类型
   * @param paraName    参数名
   * @param ac          动作上下文
   * @param headerStr   取值前缀     前缀.值主键
   * @param checkNull   如果为true，则检查提交参数是否为空，为空则返回空
   * @return            参数值
   * @throws Exception  异常
   * 2014年6月12日
   * @author 马宝刚
   */
  public Object setParams(Class<?> cls,String paraName,IActionContext ac,String headerStr,boolean checkNull) throws Exception {
      String reStr = null; //返回值
      if(headerStr==null) {
          headerStr = "";
      }
      if(headerStr.length()>0) {
          headerStr = headerStr+".";
      }
      if(cls==String.class) {
          if(checkNull) {
              reStr = ac.getParameter(headerStr+paraName);
              if(reStr.length()<1) {
                return null;
              }
              return reStr;
          }else {
            return ac.getParameter(headerStr+paraName);
          }
      }else if(cls==int.class) {
        if(checkNull) {
              reStr = ac.getParameter(headerStr+paraName);
              if(reStr.length()<1) {
                return null;
              }
              return SInteger.integerOf(reStr);
        }else {
          return SInteger.integerOf(ac.getParameter(headerStr+paraName));
        }
      }else if(cls==long.class) {
        if(checkNull) {
              reStr = ac.getParameter(headerStr+paraName);
              if(reStr.length()<1) {
                return null;
              }
              return SLong.longOf(reStr);
        }else {
          return SLong.longOf(ac.getParameter(headerStr+paraName));
        }
      }else if(cls==boolean.class) {
        if(checkNull) {
              reStr = ac.getParameter(headerStr+paraName);
              if(reStr.length()<1) {
                return null;
              }
              return SBoolean.booleanOf(reStr);
        }else {
          return SBoolean.booleanOf(ac.getParameter(headerStr+paraName));
        }
      }else if(cls==float.class) {
        if(checkNull) {
              reStr = ac.getParameter(headerStr+paraName);
              if(reStr.length()<1) {
                return null;
              }
              return SDouble.doubleOf(reStr);
        }else {
          return SDouble.doubleOf(ac.getParameter(headerStr+paraName));
        }
      }else if(cls==double.class) {
        if(checkNull) {
              reStr = ac.getParameter(headerStr+paraName);
              if(reStr.length()<1) {
                return null;
              }
              return SDouble.doubleOf(reStr);
        }else {
          return SDouble.doubleOf(ac.getParameter(headerStr+paraName));
        }
      }else if(cls==String[].class) {
        //字符串型参数
          String[] values = ac.getRequest().getParameterMap().get(headerStr+paraName);
          if(values==null || values.length<2) {
              values = BaseUtil.split(ac.getParameter(headerStr+paraName),",");
          }
          if(checkNull && values.length<1) {
            return null;
          }
          return values;
      }else if(cls==int[].class) {
          //字符串型参数
          String[] values = ac.getRequest().getParameterMap().get(headerStr+paraName);
          if(values==null || values.length<2) {
              values =  BaseUtil.split(ac.getParameter(headerStr+paraName),",");
          }
          if(checkNull && values.length<1) {
            return null;
          }
          //整型参数
          int[] paraValues = new int[values.length];
          for(int j=0;j<values.length;j++) {
              paraValues[j] = SInteger.valueOf(values[j]);
          }
          return paraValues;
      }else if(cls==long[].class) {
          //字符串型参数
          String[] values = ac.getRequest().getParameterMap().get(headerStr+paraName);
          if(values==null || values.length<2) {
              values = BaseUtil.split(ac.getParameter(headerStr+paraName),",");
          }
          if(checkNull && values.length<1) {
            return null;
          }
          //长整型参数
          long[] paraValues = new long[values.length];
          for(int j=0;j<values.length;j++) {
              paraValues[j] = SLong.valueOf(values[j]);
          }
          return paraValues;
      }else if(cls==boolean[].class) {
          //字符串型参数
          String[] values = ac.getRequest().getParameterMap().get(headerStr+paraName);
          if(values==null || values.length<2) {
              values = BaseUtil.split(ac.getParameter(headerStr+paraName),",");
          }
          if(checkNull && values.length<1) {
            return null;
          }
          //布尔参数
          boolean[] paraValues = new boolean[values.length];
          for(int j=0;j<values.length;j++) {
              paraValues[j] = SBoolean.valueOf(values[j]);
          }
          return paraValues;
      }else if(cls==float[].class) {
          //字符串型参数
          String[] values = ac.getRequest().getParameterMap().get(headerStr+paraName);
          if(values==null || values.length<2) {
              values = BaseUtil.split(ac.getParameter(headerStr+paraName),",");
          }
          if(checkNull && values.length<1) {
            return null;
          }
          //浮点参数
          float[] paraValues = new float[values.length];
          for(int j=0;j<values.length;j++) {
              paraValues[j] = (float)SDouble.valueOf(values[j]);
          }
          return paraValues;
      }else if(cls==double[].class) {
          //字符串型参数
          String[] values = ac.getRequest().getParameterMap().get(headerStr+paraName);
          if(values==null || values.length<2) {
              values = BaseUtil.split(ac.getParameter(headerStr+paraName),",");
          }
          if(checkNull && values.length<1) {
            return null;
          }
          //double参数
          double[] paraValues = new double[values.length];
          for(int j=0;j<values.length;j++) {
              paraValues[j] = SDouble.valueOf(values[j]);
          }
          return paraValues;
      }else if(cls == List.class) {
          //字符串型参数
          String[] values = ac.getRequest().getParameterMap().get(headerStr+paraName);
          if(values==null || values.length<2) {
              values = BaseUtil.split(ac.getParameter(headerStr+paraName),",");
          }
          if(checkNull && values.length<1) {
            return null;
          }
          return Arrays.asList(values);
      }else if(cls == UploadFile.class) {
          if(!ac.isMultipartRequest()) {
              return null;
          }else {
              //获取上传的文件对象
              UploadFile[] ufs = ac.getMultipartServletRequest().getUploadFiles(paraName);
              if(ufs==null || ufs.length<1) {
                  return null;
              }else {
                  return ufs[0];
              }
          }
      }else if(cls == UploadFile[].class) {
          if(!ac.isMultipartRequest()) {
            if(checkNull) {
              return null;
            }else{
              return new UploadFile[0];
            }
          }else {
              //获取上传的文件对象
            UploadFile[] reses = ac.getMultipartServletRequest().getUploadFiles(paraName);
            if(checkNull && (reses==null || reses.length<1)) {
              return null;
            }
            return reses;
          }
      }else if(cls.getSuperclass()==SessionPara.class) {
          //会话类
          Object reObj = cls.newInstance();
          ((SessionPara)reObj).setActionContext(ac);
          return reObj;
      }else {
          //VO类
        
        boolean allNull = false; //是否全部都是空值
        Object reObj = null; //构建返回值
          try {
              reObj = cls.newInstance();
          }catch(Exception e) {}
          if(cls.getSuperclass()==SessionPara.class) {
            ((SessionPara)reObj).setActionContext(ac);
            return reObj;
          }
          //获取字段数组
          Field[] fields = cls.getDeclaredFields();
          int modifiers = 0; //域
          String fieldName; //变量名
          Class<?> fCls = null ;//变量类型
          String setterMethodName; //set方法名
          Method setterMethod = null; //set方法对象
          for(int j=0;j<fields.length;j++) {
              if(fields[j]==null) {
                  continue;
              }
              modifiers =fields[j].getModifiers();
              //public private protected  static  
              if(modifiers!=1 && modifiers!=2 && modifiers!=4 && modifiers!=0 && modifiers!=8 && modifiers!=9 && modifiers!=10 && modifiers!=12) {
                  //其它域不处理
                  continue;
              }
              fieldName = fields[j].getName();
              if(fieldName==null || fieldName.length()<1) {
                  continue;
              }
              fields[j].setAccessible(true);
              fCls = fields[j].getType();
              setterMethodName = "set"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
              
              try {
                  setterMethod = cls.getDeclaredMethod(setterMethodName, fCls);
              }catch(Exception e) {}
              if(setterMethod==null) {
                  if(modifiers==1 || modifiers==9) {
                      //属于public域，直接设置变量值
                      try {
                        Object cObj = setParams(fCls,fieldName,ac,headerStr+paraName,checkNull);
                        if(cObj!=null) {
                          allNull = false;
                        }
                          fields[j].set(reObj,cObj);
                      }catch(Exception e) {}
                  }
              }else {
                  try {
                    //属性值
                    Object cObj = setParams(fCls,fieldName,ac,headerStr+paraName,checkNull);
                    if(cObj!=null) {
                      allNull = false;
                    }
                      setterMethod.invoke(reObj, cObj);
                  }catch(Exception e) {}
              }
          }
          if(checkNull && allNull) {
            return null;
          }
          return reObj;
      }
  }
  
  /**
   * 获取下载文件处理类
   * @return 下载文件处理类
   * 2014年8月17日
   * @author 马宝刚
   */
  public DownloadFile download() {
    noReturnInfo = true;
    return ac.getDownloadFile();
  }
  
  /**
   * 获取下载文件处理类
   * @param basePath 文件根路径
   * @return 下载文件处理类
   * 2016年11月14日
   * @author MBG
   */
  public DownloadFile download(String basePath) {
    noReturnInfo = true;
    return ac.getDownloadFile(basePath);
  }
  
  /**
   * 执行下载文件
   * @param filePath    服务器端文件全路径
   * @param fileName    下载到客户端时保存的文件名
   * @param inPage      是否在浏览器中打开
   * @param afterDelete 下载后是否删除服务器端文件
   * @throws Exception  执行发生异常
   * 2018年11月16日
   * @author MBG
   */
  public void downloadFile(String filePath,String fileName,boolean inPage,boolean afterDelete)  throws Exception {
    noReturnInfo = true;
    ac.getDownloadFile().downloadFile(filePath,fileName,null,inPage,afterDelete);
  }
  
  /**
   * 执行显示指定压缩后的图片
   * @param path 图片相对路径（相对于file_sources）
   * @param qality 压缩比率  0~10 之间，数值越大，文件越大，越清晰
   * @throws Exception 异常
   * 2016年4月28日
   * @author MBG
   */
  public void showImg(String path,String qality) throws Exception {
    showImg(path,sint(qality));
  }
  
  /**
   * 执行显示指定压缩后的图片
   * @param path 图片相对路径（相对于file_sources）
   * @param qality 压缩比率  0~10 之间，数值越大，文件越大，越清晰
   * @throws Exception 异常
   * 2016年4月28日
   * @author MBG
   */
  public void showImg(String path,int qality) throws Exception  {
    showImg(path,qality/10);
  }
  
  /**
   * 执行显示指定压缩后的图片
   * @param path 图片相对路径（相对于file_sources）
   * @param qality 压缩比率  0~1之间，数值越大，文件越大，越清晰
   * @throws Exception 异常
   * 2016年4月28日
   * @author MBG
   */
  public void showImg(String path,float qality) throws Exception {
    (new ImgShow(ac.getResponse(),sourcePath())).show(path,qality);
  }
  
  
  /**
   * 设置输出编码
   * @param enc 输出编码
   * 2014年8月28日
   * @author 马宝刚
   */
  public void setCharacterEncoding(String enc) {
    $$().setCharacterEncoding(enc);
  }
  
  /**
   * 获取客户IP地址
   * @return 客户IP地址
   * 2016年9月28日
   * @author MBG
   */
  public String uip() {
    return ac.cip();
  }
  
  /**
   * 从页面请求对象中批量获取参数值 （通常用于提交数据库） 
   * 注意：需要从页面请求中获取值的主键开头 为@
   * @param keys 参数主键数组
   * @return 参数值数组
   * 2016年10月17日
   * @author MBG
   */
  public String[] $(String[] keys) {
    if(keys==null || keys.length<1) {
      return new String[0];
    }
    //构造返回值
    String[] reArr = new String[keys.length];
    for(int i=0;i<keys.length;i++) {
      keys[i] = str(keys[i]);
      if(keys[i].startsWith("@")) {
        reArr[i] = $(keys[i].substring(1));
      }else {
        reArr[i] = keys[i];
      }
    }
    return reArr;
  }
  
  /**
   * 获取用户主键（手动更新数据库时使用）
   * @return 用户主键
   * 2016年10月17日
   * @author MBG
   */
  public String uid() {
    return str($$$("_user_id_"));
  }
  
  /**
   * 获取用户名（手动更新数据库时使用）
   * @return 用户名
   * 2016年10月17日
   * @author MBG
   */
  public String uname() {
    return str($$$("_user_name_"));
  }
  
  /**
   * 向页面发送错误代码
   * @param code 错误代码  404 500 503 等等
   * @throws Exception 异常
   * 2016年10月20日
   * @author MBG
   */
  public void sendError(int code) throws Exception {
    $$().sendError(code,$());
  }
  
  /**
   * 向页面发送错误代码
   * @param code 错误代码  404 500 503 等等
   * @param msg  错误信息
   * @throws Exception 异常
   * 2018年04月19日
   * @author MBG
   */
  public void sendError(int code,String msg) throws Exception {
    $$().sendError(code,msg,$());
  }
  
  /**
   * 获取动作上下文
   * @return 动作上下文
   * 2016年10月24日
   * @author MBG
   */
  public String ctx() {
    return $().getContextPath();
  }
  
  /**
   * 获取集群中指定群组服务器的上下文信息
   * @param groupName 群组名
   * @return 指定群组服务器的上下文信息
   * 2017年4月26日
   * @author MBG
   */
  public String ctx(String groupName) {
    //获取集群管理器
    ICluster clu = bean(ICluster.class);
    if(clu==null || clu.disabled()) {
      return $().getContextPath();
    }
    //获取目标服务器信息
    ServerInfoVO master = clu.getMasterServer(groupName);
    
    return str(master.context);
  }
  
  /**
   * 获取请求数据容器
   * @return 请求数据容器
   * 2019年12月3日
   * @author MBG
   */
  public Map<String,String> $m(){
    return ac.getSingleParameterMap();
  }
  
  /**
   * 获取标准参数容器
   * @return 标准参数容器
   * 2019年12月5日
   * @author MBG
   */
  public Map<String,String[]> $am(){
    return ac.getRequest().getParameterMap();
  }
  
  /**
   * 从Request.getParameter中获取参数值，如果参数值为空，则返回默认值
   * @param key       参数主键
   * @param defValue  参数值
   * @return 参数值或默认值
   * 2016年10月26日
   * @author MBG
   */
  public String $d(String key,String defValue) {
    // 先从页面请求中获取值
    String reStr = ac.getParameter(key);
    if (reStr.length() < 1) {
      reStr = SString.valueOf(getModelParameterMap().get(key));
    }
    if(reStr.length()<1) {
      return defValue;
    }
    return reStr;
  }
  
  /**
   * 是否处理完当前动作后，就不再处理页面中后续的页面块动作
   * @return 如题
   * 2016年11月23日
   * @author MBG
   */
  @Override
  public boolean isOver() {
    return isOver;
  }
  
  /**
   * 设置是否处理完当前动作后，就不再处理页面中后续的页面块动作
   * @param isOver 如题
   * 2016年11月23日
   * @author MBG
   */
  @Override
  public void over(boolean isOver) {
    this.isOver = isOver;
  }
  
  /**
   * 是否在当前动作块中加载了整个页面
   * @return 如题
   * 2016年11月23日
   * @author MBG
   */
  @Override
  public boolean loadAllPage() {
    return loadAllPage;
  }
  
  /**
   * 在当前动作类中处理好数据后，跳转到下一个动作
   * 设置数据方法：  request.setAttribute(key,value);  即  $$(key,value);
   * 
   * 通常下一个动作是JSP页面路径
   * 
   * 这个方法主要用于将框架嵌入老的，现有的项目中，融合到老的项目中
   * 
   * @param actionPath  动作路径（JSP文件路径）
   * @throws Exception 异常
   * 2017年6月7日
   * @author MBG
   */
  public void forward(String actionPath) throws Exception {
    ac.getRequest().getRequestDispatcher(actionPath).forward(ac.getRequest(),ac.getResponse());
  }
  
  /**
   * 获取提交参数类型
   * @return -1未设置 0字符串  1xml  2json  3对象容器  4未知
   * 2018年3月2日
   * @author MBG
   */
  public int getPostType() {
    return postType;
  }
    
  /**
   * 获取整理后的提交参数对象
   * @return 整理后的提交参数对象
   * 2018年3月2日
   * @author MBG
   */
  public Object getPostObject() {
    return postObject;
  }
  
  /**
   * 从相对网站根路径中加载指定文件（并解析文件中的动态代码），返回解析后的内容
   * @param subPath 相对路径（相对网站根路径）
   * @return        读取并解析后的文件内容
   * 2019年8月20日
   * @author MBG
   */
  public String htmlContent(String subPath) {
    return htmlContent(subPath,null);
  }
  
  /**
   * 从相对网站根路径中加载指定文件（并解析文件中的动态代码），返回解析后的内容
   * @param subPath       相对路径（相对网站根路径）
   * @param pageParaMap   解析内容中所需要的参数容器 比如<%=p:key%>
   * @return              读取并解析后的文件内容
   * 2019年8月20日
   * @author MBG
   */
  public String htmlContent(String subPath,Map<String,String> pageParaMap) {
    //获取字节过滤器
    ByteFilter bf = bean(ByteFilter.class);
    if(bf==null) {
      warning("----------Not Find The ByteFilter");
      return "";
    }
    try {
      return bf.fileContent(subPath,$(),$$(),pageParaMap);
    }catch(Exception e) {
      e.printStackTrace();
    }
    return "";
  }
  
  /**
   * 是否存在报文头
   * @param key 报文头主键
   * @return    报文头值
   * 2019年12月13日
   * @author MBG
   */
  public boolean hasHeader(String key) {
    if(ac==null) {
      return false;
    }
    return ac.getRequest().getHeaderMap().containsKey(key);
  }
    
  /**
   * 获取指定的头值
   * @param key 报文头主键
   * @return    报文头值
   * 2019年12月13日
   * @author MBG
   */
  public String header(String key) {
    if(ac==null) {
      return "";
    }
    return str(ac.getRequest().getHeader(key));
  }
  
  /**
   * 获取指定报文头值序列
   * @param key 报文头主键
   * @return    报文头值序列
   * 2019年12月13日
   * @author MBG
   */
  public List<String> headers(String key) {
    //构建返回值
    List<String> res = new ArrayList<String>();
    if(ac==null) {
      return res;
    }
    //返回头信息迭代器
    Enumeration<String> values = ac.getRequest().getHeaders(key);
    if(values==null) {
      return res;
    }
    while(values.hasMoreElements()) {
      res.add(values.nextElement());
    }
    return res;
  }
  
  /**
   * 添加一个同名报文头值
   * @param key     报文头主键
   * @param value   报文头值
   * 2019年12月13日
   * @author MBG
   */
  public void addHeader(String key,String value) {
    if(ac==null) {
      return;
    }
    ac.getResponse().addHeader(key,value);
  }
  
  /**
   * 设置报文头值
   * @param key    报文头主键
   * @param value  报文头值
   * 2019年12月13日
   * @author MBG
   */
  public void setHeader(String key,String value) {
    if(ac==null) {
      return;
    }
    ac.getResponse().setHeader(key,value);
  }

  /**
   * 在输出页面之前调用该方法
   * 该方法可以在继承该类的父类中覆盖该方法，用来统一整理即将输出的页面
   * @param vh 即将输出的页面对象
   * @return   处理后的页面对象
   */
  public IViewHandler beforeOutView(IViewHandler vh){
    return vh;
  }

  /**
   * 在输出页面之前调用该方法
   * 该方法可以在继承该类的父类中覆盖该方法，用来统一整理即将输出的Json数据
   * @param json 即将输出的json数据对象
   * @return   处理后的json数据对象
   */
  public Json beforeOutJson(Json json){
    if(json!=null && !json.isList() && !json.containsKey("status")) {
      //设置默认返回值
      json.put("status","1");
    }
    return json;
  }
}
